diff --git a/Configuration/GlobalAssemblyInfo.cs b/Configuration/GlobalAssemblyInfo.cs
index 36594fba8..2f0e628a2 100644
--- a/Configuration/GlobalAssemblyInfo.cs
+++ b/Configuration/GlobalAssemblyInfo.cs
@@ -15,7 +15,7 @@ internal static class RevisionClass
public const string Major = "3";
public const string Minor = "8";
public const string Build = "0";
- public const string Revision = "2932";
+ public const string Revision = "2934";
public const string MainVersion = Major + "." + Minor;
public const string FullVersion = Major + "." + Minor + "." + Build + "." + Revision;
diff --git a/Configuration/Version.defs b/Configuration/Version.defs
index 1d054bb10..6dbe8a6f3 100644
--- a/Configuration/Version.defs
+++ b/Configuration/Version.defs
@@ -1,4 +1,4 @@
%COREVERSION%=0
-%REVISION%=2932
+%REVISION%=2934
%MINOR%=8
%MAJOR%=3
diff --git a/InteractiveDataDisplay_OneDll/.gitignore b/InteractiveDataDisplay_OneDll/.gitignore
new file mode 100644
index 000000000..940794e60
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/.gitignore
@@ -0,0 +1,288 @@
+## 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 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# 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
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+**/Properties/launchSettings.json
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.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
+
+# 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
+
+# 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
+# TODO: 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
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/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
+
+# 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
+
+# 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
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Typescript v1 declaration files
+typings/
+
+# 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
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
diff --git a/InteractiveDataDisplay_OneDll/InteractiveDataDisplay.WPF.nuspec b/InteractiveDataDisplay_OneDll/InteractiveDataDisplay.WPF.nuspec
new file mode 100644
index 000000000..d75774387
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/InteractiveDataDisplay.WPF.nuspec
@@ -0,0 +1,43 @@
+
+
+
+ InteractiveDataDisplay.WPF
+ 1.1.0
+ InteractiveDataDipslay.WPF
+ Sergey Berezin, Vassily Lyutsarev, Nikita Skoblov, Natalia Stepanova
+ Microsoft; MSU ITIS Lab
+ false
+ https://github.com/Microsoft/InteractiveDataDisplay.WPF/blob/master/LICENSE
+ https://github.com/Microsoft/InteractiveDataDisplay.WPF
+ Interactive Data Display for WPF is a set of controls for adding interactive visualization of dynamic data to your application.
+ Interactive Data Dipslay for WPF allows to create line graphs, bubble charts, heat maps and other complex 2D plots which are very common in scientific software. It integrates well with Bing Maps control to show data on a geographic map in latitude/longitude coordinates. The controls can also be operated programmatically.
+ Copyright 2017 Microsoft Corporation
+ idd wpf visualization plot plots plotting chart charting data interactive datavisualization
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/InteractiveDataDisplay_OneDll/LICENSE b/InteractiveDataDisplay_OneDll/LICENSE
new file mode 100644
index 000000000..5b677b6f6
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/LICENSE
@@ -0,0 +1,23 @@
+ InteractiveDataDisplay.WPF
+
+ MIT License
+
+ Copyright (c) Microsoft Corporation. All rights reserved.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE
diff --git a/InteractiveDataDisplay_OneDll/README.md b/InteractiveDataDisplay_OneDll/README.md
new file mode 100644
index 000000000..bd646c2c5
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/README.md
@@ -0,0 +1,68 @@
+Interactive Data Display for WPF
+================================
+
+Interactive Data Display for WPF is a set of controls for adding interactive visualization of dynamic data to your application. It allows to create line graphs, bubble charts, heat maps and other complex 2D plots which are very common in scientific software. Interactive Data Display for WPF integrates well with Bing Maps control to show data on a geographic map in latitude/longitude coordinates. The controls can also be operated programmatically.
+
+How to install
+----------
+The IDD for WPF can be [installed from NuGet](https://www.nuget.org/packages/InteractiveDataDisplay.WPF/):
+```
+PM > Install-Package InteractiveDataDisplay.WPF -Version 1.0.0
+```
+
+How to use
+----------
+Drawing with Interactive Data Display is easy.
+
+One line of C# code to draw simple linegraph:
+````c#
+linegraph.Plot(x,y); // x and y are IEnumerable
+````
+And corresponding XAML snippet:
+````xaml
+
+
+
+````
+If we get arrays `x` and `y` as:
+````c#
+var x = Enumerable.Range(0, 1001).Select(i => i / 10.0).ToArray();
+var y = x.Select(v => Math.Abs(v) < 1e-10 ? 1 : Math.Sin(v) / v).ToArray();
+````
+We will receive:
+
+
+
+Other Interactive Data Display samples:
+
+
+
+
+
+
+
+See the source code [here](https://github.com/Microsoft/InteractiveDataDisplay.WPF/tree/master/samples).
+
+Licensing
+---------
+
+Interactive Data Display for WPF is under the [MIT license](https://github.com/Microsoft/InteractiveDataDisplay.WPF/blob/master/LICENSE).
+
+Other
+-----
+There is also [Interactive Data Display](https://github.com/predictionmachines/InteractiveDataDisplay) for Javascript. You can see interactive samples [here](http://predictionmachines.github.io/InteractiveDataDisplay).
+
+Contributing
+------------
+
+This project welcomes contributions and suggestions. Most contributions require you to agree to a
+Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
+the rights to use your contribution. For details, visit https://cla.microsoft.com.
+
+When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
+a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
+provided by the bot. You will only need to do this once across all repos using our CLA.
+
+This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
+For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
+contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
diff --git a/InteractiveDataDisplay_OneDll/images/barchart.PNG b/InteractiveDataDisplay_OneDll/images/barchart.PNG
new file mode 100644
index 000000000..aad5f5253
Binary files /dev/null and b/InteractiveDataDisplay_OneDll/images/barchart.PNG differ
diff --git a/InteractiveDataDisplay_OneDll/images/heatmap.PNG b/InteractiveDataDisplay_OneDll/images/heatmap.PNG
new file mode 100644
index 000000000..777fcaa53
Binary files /dev/null and b/InteractiveDataDisplay_OneDll/images/heatmap.PNG differ
diff --git a/InteractiveDataDisplay_OneDll/images/line.PNG b/InteractiveDataDisplay_OneDll/images/line.PNG
new file mode 100644
index 000000000..af5d6f065
Binary files /dev/null and b/InteractiveDataDisplay_OneDll/images/line.PNG differ
diff --git a/InteractiveDataDisplay_OneDll/images/map.PNG b/InteractiveDataDisplay_OneDll/images/map.PNG
new file mode 100644
index 000000000..724745bda
Binary files /dev/null and b/InteractiveDataDisplay_OneDll/images/map.PNG differ
diff --git a/InteractiveDataDisplay_OneDll/images/markers.PNG b/InteractiveDataDisplay_OneDll/images/markers.PNG
new file mode 100644
index 000000000..bed2535ec
Binary files /dev/null and b/InteractiveDataDisplay_OneDll/images/markers.PNG differ
diff --git a/InteractiveDataDisplay_OneDll/images/sinline.PNG b/InteractiveDataDisplay_OneDll/images/sinline.PNG
new file mode 100644
index 000000000..dcb12c7bc
Binary files /dev/null and b/InteractiveDataDisplay_OneDll/images/sinline.PNG differ
diff --git a/InteractiveDataDisplay_OneDll/samples/BarChartSample/App.config b/InteractiveDataDisplay_OneDll/samples/BarChartSample/App.config
new file mode 100644
index 000000000..88fa4027b
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BarChartSample/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/InteractiveDataDisplay_OneDll/samples/BarChartSample/App.xaml b/InteractiveDataDisplay_OneDll/samples/BarChartSample/App.xaml
new file mode 100644
index 000000000..b85fb66d1
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BarChartSample/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/InteractiveDataDisplay_OneDll/samples/BarChartSample/App.xaml.cs b/InteractiveDataDisplay_OneDll/samples/BarChartSample/App.xaml.cs
new file mode 100644
index 000000000..04e2769ee
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BarChartSample/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 BarChartSample
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/InteractiveDataDisplay_OneDll/samples/BarChartSample/BarChartSample.csproj b/InteractiveDataDisplay_OneDll/samples/BarChartSample/BarChartSample.csproj
new file mode 100644
index 000000000..5348a1fda
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BarChartSample/BarChartSample.csproj
@@ -0,0 +1,103 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {F36872BF-8818-4019-A207-2B381C33C370}
+ WinExe
+ BarChartSample
+ BarChartSample
+ v4.5.2
+ 512
+ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 4
+ 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
+
+
+
+
+
+
+
+ {2d34544c-2df2-4b20-a43a-6c8d2df3dd82}
+ InteractiveDataDisplay.WPF
+
+
+
+
\ No newline at end of file
diff --git a/InteractiveDataDisplay_OneDll/samples/BarChartSample/MainWindow.xaml b/InteractiveDataDisplay_OneDll/samples/BarChartSample/MainWindow.xaml
new file mode 100644
index 000000000..55370aa49
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BarChartSample/MainWindow.xaml
@@ -0,0 +1,17 @@
+
+
+
+
+ Bar chart sample
+
+
+
+
+
diff --git a/InteractiveDataDisplay_OneDll/samples/BarChartSample/MainWindow.xaml.cs b/InteractiveDataDisplay_OneDll/samples/BarChartSample/MainWindow.xaml.cs
new file mode 100644
index 000000000..7612942f6
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BarChartSample/MainWindow.xaml.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Windows;
+
+namespace BarChartSample
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+
+ int N = 100;
+ double[] y = new double[N];
+
+ Random r = new Random();
+ double k;
+
+ for (int i = 0; i < N; i++)
+ {
+ k = r.NextDouble();
+ y[i] = k < 0.5 ? r.Next(100) : -r.Next(100);
+ }
+ barChart.PlotBars(y);
+ }
+ }
+}
diff --git a/InteractiveDataDisplay_OneDll/samples/BarChartSample/Properties/AssemblyInfo.cs b/InteractiveDataDisplay_OneDll/samples/BarChartSample/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..244a26027
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BarChartSample/Properties/AssemblyInfo.cs
@@ -0,0 +1,55 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("BarChartSample")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("BarChartSample")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+//In order to begin building localizable applications, set
+//CultureYouAreCodingWith in your .csproj file
+//inside a . For example, if you are using US english
+//in your source files, set the to en-US. Then uncomment
+//the NeutralResourceLanguage attribute below. Update the "en-US" in
+//the line below to match the UICulture setting in the project file.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[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)
+)]
+
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/InteractiveDataDisplay_OneDll/samples/BarChartSample/Properties/Resources.Designer.cs b/InteractiveDataDisplay_OneDll/samples/BarChartSample/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..bc4a8188b
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BarChartSample/Properties/Resources.Designer.cs
@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+//
+// 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 BarChartSample.Properties
+{
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [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()
+ {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [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("BarChartSample.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [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/InteractiveDataDisplay_OneDll/samples/BarChartSample/Properties/Resources.resx b/InteractiveDataDisplay_OneDll/samples/BarChartSample/Properties/Resources.resx
new file mode 100644
index 000000000..af7dbebba
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BarChartSample/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/InteractiveDataDisplay_OneDll/samples/BarChartSample/Properties/Settings.Designer.cs b/InteractiveDataDisplay_OneDll/samples/BarChartSample/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..1eaf99ed6
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BarChartSample/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+//
+// 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 BarChartSample.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/InteractiveDataDisplay_OneDll/samples/BarChartSample/Properties/Settings.settings b/InteractiveDataDisplay_OneDll/samples/BarChartSample/Properties/Settings.settings
new file mode 100644
index 000000000..033d7a5e9
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BarChartSample/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/InteractiveDataDisplay_OneDll/samples/BingMapsSample/App.config b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/App.config
new file mode 100644
index 000000000..88fa4027b
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/InteractiveDataDisplay_OneDll/samples/BingMapsSample/App.xaml b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/App.xaml
new file mode 100644
index 000000000..f26edcfb1
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/InteractiveDataDisplay_OneDll/samples/BingMapsSample/App.xaml.cs b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/App.xaml.cs
new file mode 100644
index 000000000..24239e5e3
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/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 BingMapsSample
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/InteractiveDataDisplay_OneDll/samples/BingMapsSample/BingMapsSample.csproj b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/BingMapsSample.csproj
new file mode 100644
index 000000000..e6f999861
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/BingMapsSample.csproj
@@ -0,0 +1,117 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {C724F25D-6F77-48C0-A791-28E34E6B0F0C}
+ WinExe
+ Properties
+ BingMapsSample
+ BingMapsSample
+ v4.5.2
+ 512
+ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 4
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\..\sln\packages\Microsoft.Maps.MapControl.WPF.1.0.0.3\lib\net40-Client\Microsoft.Maps.MapControl.WPF.dll
+ True
+
+
+
+
+
+
+
+
+
+
+ 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
+
+
+
+
+
+
+
+
+ {2d34544c-2df2-4b20-a43a-6c8d2df3dd82}
+ InteractiveDataDisplay.WPF
+
+
+
+
+
\ No newline at end of file
diff --git a/InteractiveDataDisplay_OneDll/samples/BingMapsSample/MainWindow.xaml b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/MainWindow.xaml
new file mode 100644
index 000000000..7c5773180
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/MainWindow.xaml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/InteractiveDataDisplay_OneDll/samples/BingMapsSample/MainWindow.xaml.cs b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/MainWindow.xaml.cs
new file mode 100644
index 000000000..c6f6c9cdd
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/MainWindow.xaml.cs
@@ -0,0 +1,38 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Windows;
+
+namespace BingMapsSample
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+ plot.Tag = bingmap;
+
+ List lat = new List(), lon = new List(), data = new List();
+ using (var reader = new StreamReader(@"..\..\crop_area.csv"))
+ {
+ while (!reader.EndOfStream)
+ {
+ var line = reader.ReadLine();
+ var values = line.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
+ lon.Add(Double.Parse(values[0], CultureInfo.InvariantCulture));
+ lat.Add(Double.Parse(values[1], CultureInfo.InvariantCulture));
+ data.Add(Double.Parse(values[2], CultureInfo.InvariantCulture));
+ }
+ }
+
+ markers.PlotColor(lon.ToArray(), lat.ToArray(), data.ToArray());
+ }
+ }
+}
diff --git a/InteractiveDataDisplay_OneDll/samples/BingMapsSample/Properties/AssemblyInfo.cs b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..2cdf6ccce
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/Properties/AssemblyInfo.cs
@@ -0,0 +1,55 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("BingMapsSample")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("BingMapsSample")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+//In order to begin building localizable applications, set
+//CultureYouAreCodingWith in your .csproj file
+//inside a . For example, if you are using US english
+//in your source files, set the to en-US. Then uncomment
+//the NeutralResourceLanguage attribute below. Update the "en-US" in
+//the line below to match the UICulture setting in the project file.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[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)
+)]
+
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/InteractiveDataDisplay_OneDll/samples/BingMapsSample/Properties/Resources.Designer.cs b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..9535f6e19
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/Properties/Resources.Designer.cs
@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+//
+// 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 BigMapsSample.Properties
+{
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [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()
+ {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [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("BigMapsSample.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [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/InteractiveDataDisplay_OneDll/samples/BingMapsSample/Properties/Resources.resx b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/Properties/Resources.resx
new file mode 100644
index 000000000..af7dbebba
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/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/InteractiveDataDisplay_OneDll/samples/BingMapsSample/Properties/Settings.Designer.cs b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..670331bdd
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+//
+// 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 BigMapsSample.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/InteractiveDataDisplay_OneDll/samples/BingMapsSample/Properties/Settings.settings b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/Properties/Settings.settings
new file mode 100644
index 000000000..033d7a5e9
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/InteractiveDataDisplay_OneDll/samples/BingMapsSample/crop_area.csv b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/crop_area.csv
new file mode 100644
index 000000000..47e4cf240
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/crop_area.csv
@@ -0,0 +1,2946 @@
+-87.14859772;33.03834915;0
+-86.63339996;34.01269913;0.00217824
+-85.70469666;32.08162689;0.001499976
+-86.67725372;31.74399948;0.003620297
+-85.35935211;32.9185257;0
+-86.69689941;32.86545181;0.001801205
+-88.20185089;32.00394821;0
+-87.81974792;31.59149933;0
+-85.90750122;33.29457474;0
+-85.59629822;33.7167511;0.001394566
+-86.25894928;32.92907333;0
+-86.32274628;31.74575043;0.004357508
+-87.85154724;34.44314957;0.004178859
+-87.94387817;32.83895111;0.00145153
+-86.90245056;33.54432678;0
+-88.10984802;33.79135132;0.003616599
+-85.34899902;32.57804871;0
+-86.65589905;32.17839813;0.003917458
+-85.72805023;32.41439819;0.00409487
+-87.78990173;32.26077652;0.000639687
+-87.92079926;34.11780167;0
+-88.18650055;30.74489975;0.001013714
+-86.21414948;32.23270035;0.001978449
+-87.2716217;32.59090042;0.001954537
+-88.08865356;33.26255035;0.005318123
+-85.16970062;32.28604889;0.001705467
+-86.32160187;33.68040085;0.00078647
+-85.80102539;32.79990005;0
+-87.29485321;33.7574501;0
+-87.26184845;32.046875;0.001934048
+-87.37322235;34.14965057;0
+-109.5223007;35.24095154;0
+-112.0443497;35.63372421;0
+-110.8554993;33.73215103;0
+-109.7823486;33.03855133;0.000573788
+-109.2700272;33.10224915;0
+-113.647377;35.61334991;0
+-110.2884979;35.28625107;0
+-111.8898468;31.96990013;0
+-110.9089966;31.53199959;0
+-112.4041748;34.70529938;0
+-114.0678024;32.7504425;0.000538393
+-91.38365173;34.26305008;0.003161388
+-91.79750061;33.20220184;0.013569994
+-92.35449982;36.23625183;0
+-94.24210358;36.30054855;0
+-93.07749939;36.30625153;0
+-92.16917419;33.43534851;0
+-92.53822327;33.55054855;0
+-93.58204651;36.31034851;0
+-93.19419861;34.05889893;0
+-92.02185059;35.53522491;0
+-92.21414948;33.88410187;0
+-93.23435211;33.23625183;0
+-94.19239807;35.55602646;0.009185156
+-90.77185059;35.29624939;0.017760006
+-92.64745331;33.97534943;0
+-92.35684967;35.11989975;0
+-93.88870239;35.48685074;0
+-91.80449677;36.37625122;0
+-93.09919739;34.58124924;0
+-92.44317627;34.27759933;0
+-93.70670319;33.74240112;0
+-93.03829956;34.32772446;0
+-94.03794861;34.05157471;0
+-91.94454956;36.06415176;0
+-91.05357361;36.07097626;0
+-91.69879913;33.97850037;0.004176319
+-93.70414734;36.03350067;0
+-92.64982605;36.2804985;0
+-93.85820007;33.31639862;0.04131743
+-93.66425323;34.54315186;0
+-93.29360199;33.69874954;0
+-93.23204803;35.92494965;0
+-92.84230042;33.58475113;0
+-92.92479706;34.94335175;0
+-93.65505219;34.14757538;0
+-94.19985199;34.45940018;0
+-93.05415344;35.42575073;0
+-92.39234924;34.7519989;0
+-90.77572632;35.00814819;0
+-92.65950012;34.6358757;0
+-94.07945251;34.88264847;0
+-92.68115234;35.91685104;0
+-94.23795319;35.19205093;0
+-94.21765137;33.97154999;0
+-92.12684631;35.91889954;0
+-92.52884674;33.19630051;0
+-92.54585266;35.57720184;0
+-94.220047;35.99377441;0
+-91.73325348;35.27890015;0
+-121.9217987;37.67757416;0.000847377
+-119.8101501;38.62889862;0
+-120.5492477;38.46559906;0
+-120.5059509;38.17164993;0
+-121.9980469;37.90987396;0.005642753
+-123.890152;41.69127655;0
+-120.5111771;38.78570175;0
+-123.8980484;40.73345184;0
+-115.285347;33.02610016;0.000224565
+-117.2005463;36.62549973;0
+-122.7126007;39.12480164;0
+-120.6631012;40.44647598;0
+-118.298172;34.26347351;0
+-122.6778488;38.06425095;0
+-119.8471985;37.5271492;0
+-123.4216537;39.38570023;0
+-120.7268982;41.59080124;0
+-118.7369995;38.0882988;0
+-121.0956497;36.34690094;0
+-122.3560028;38.50984955;0
+-121.0003357;39.2710495;0
+-117.7664032;33.66650009;0
+-120.7421494;39.01385117;0
+-120.7967987;40.02339935;0
+-116.055397;33.75289917;8.67168E-05
+-121.1194534;36.59429932;0
+-116.8377991;33.0196991;0
+-122.3984985;37.81909943;0
+-120.4091721;35.34640121;0
+-122.2912521;37.40790176;0
+-120.0454712;34.73550034;0
+-121.7052536;37.19210052;0
+-121.9485016;37.06840134;0
+-122.1912003;40.73529816;0
+-120.5155258;39.58217621;0
+-122.5621262;41.50479889;0
+-122.9457016;38.46125031;0
+-122.2154236;40.12167358;0.000370638
+-123.0228271;40.66040039;0
+-119.9507523;38.03504944;0
+-119.0536728;34.47320175;0
+-105.747673;37.55410004;0
+-104.3806992;39.65227509;0.000972744
+-106.9785004;37.20755005;0
+-106.264473;38.74005127;0
+-105.6604004;39.70849991;0
+-106.1977768;37.19817352;0
+-105.4532471;37.31534958;0
+-103.7794037;38.31790161;0.00099019
+-105.4223022;38.07842636;0
+-104.9216537;39.71142578;0
+-108.4516525;37.68935013;0
+-104.9943466;39.34794998;0
+-106.6443253;39.64144897;0
+-104.1882477;39.21657562;0
+-105.4753036;38.4776001;0
+-107.5255051;39.72862625;0
+-105.5480499;39.84069824;0
+-106.1399994;40.08560181;0
+-106.9406509;38.70185089;0
+-107.2917023;37.78504944;0
+-104.9253464;37.64664841;0.000196433
+-106.3559723;40.6711998;0
+-105.2227478;39.52190018;0
+-107.9298477;37.31980133;0
+-106.3563538;39.21784973;0
+-104.1144714;37.40520096;0
+-106.9188004;37.67615128;0
+-108.1817017;40.61159897;0
+-108.508049;37.31887436;0
+-107.8392029;38.1147995;0
+-105.7592468;39.13105011;0
+-106.9298019;39.18225098;0
+-108.0437012;39.93757629;0
+-106.3744278;37.61555099;0
+-107.0333481;40.46115112;0
+-106.2238998;38.09704971;0
+-107.7265472;37.80224991;0
+-108.3899231;37.96450043;0
+-106.1078033;39.64839935;0
+-105.1373749;38.88880157;0
+-73.40844727;41.32450104;0
+-72.71914673;41.7975502;0
+-73.20310211;41.75954819;0
+-72.52964783;41.44984818;0
+-72.92737579;41.40394974;0
+-72.12760162;41.49459839;0
+-72.30975342;41.81135178;0
+-72.01877594;41.83140182;0
+-82.35579681;29.6827507;0.001608522
+-82.25440216;30.36014938;0
+-85.69020081;30.24580002;0
+-82.23262787;29.93052483;0
+-80.717453;28.30644989;0
+-80.47814941;26.14539909;0
+-85.16120148;30.40360069;0.002478803
+-81.96884918;26.90184975;0
+-82.48885345;28.85510063;0
+-81.8151474;29.95439911;0
+-81.35962677;26.15979958;0
+-81.81030273;27.18585014;0
+-83.17375183;29.55597496;0
+-81.71524811;30.34474945;0
+-81.3131485;29.46559906;0
+-84.78199768;29.82142448;0
+-82.81447601;29.74710083;0.003582791
+-81.21869659;26.98914909;0
+-85.21295166;29.93959999;0
+-81.80964661;27.49135017;0
+-81.22309875;26.60560036;0
+-82.36804962;28.56389999;0
+-81.25244904;27.33909988;0
+-82.35295105;27.90912437;0
+-80.60154724;27.70885086;0
+-83.84030151;30.37585068;0.008887643
+-83.12352753;30.04125023;0.00287838
+-81.654953;28.81122589;0
+-81.9177475;26.55285072;0
+-84.34625244;30.47929955;0
+-82.78434753;29.28790092;0.000838266
+-84.84470367;30.28874969;0
+-82.40035248;27.42675018;0
+-82.08760071;29.2378006;0
+-80.48314667;27.10980034;0
+-81.16349792;25.46039963;0
+-81.74434662;30.55055046;0
+-80.93887329;27.30080032;0
+-81.26129913;28.56615067;0
+-81.2594986;27.99477577;0
+-80.45862579;26.64559937;0
+-82.4306488;28.3247509;0
+-82.72290039;27.8960495;0
+-81.62439728;28.00225067;0
+-81.74524689;29.58180046;0
+-80.43975067;27.38184929;0
+-87.04820251;30.67485046;0
+-82.13269806;28.63607407;0
+-83.65585327;29.98344994;0
+-82.35534668;30.03289986;0
+-81.20704651;29.01989937;0
+-84.3946991;30.13647461;0
+-82.42774963;31.56380081;0.007676785
+-83.24324799;33.05754852;0
+-83.50174713;34.34484863;0
+-83.70314789;34.01192474;0
+-83.69114685;32.8083992;0
+-83.31855011;32.42810059;0.011500069
+-83.97064972;33.31470108;0
+-81.6664505;30.93995094;0
+-85.07389832;33.61925125;0
+-85.12245178;34.87779999;0.002889416
+-82.15955353;30.71139908;0
+-81.1174469;31.98114967;0
+-84.81860352;32.38272476;0
+-84.45835114;34.24295044;0
+-83.39084625;33.94544983;0
+-84.35150146;33.50059891;0
+-82.69494629;30.88297462;0.000965352
+-84.55719757;33.91025162;0
+-82.23239899;33.52157593;0
+-84.75624847;33.35105133;0
+-83.95645142;32.69485092;0
+-85.48445129;34.80479813;0
+-84.15184784;34.47582626;0
+-84.62057495;30.8845253;0
+-84.74220276;33.69004822;0
+-82.82089996;34.12265015;0
+-84.23242188;34.80939865;0
+-84.50644684;33.4041481;0
+-84.10489655;34.19279861;0
+-83.22515106;34.38010025;0
+-84.40937042;33.84429932;0
+-84.42434692;34.7016983;0
+-82.59400177;33.2242012;0
+-81.51605225;31.23349953;0
+-83.18977356;33.55830002;0
+-84.03775024;33.96025085;0
+-83.51010132;34.62712479;0
+-83.83914948;34.30609894;0.001190748
+-83.01129913;33.27434921;0
+-85.21179962;33.7791481;0
+-84.93855286;32.72757339;0
+-85.11634827;33.27875137;0
+-84.13899994;33.47254944;0
+-83.67745209;32.48735046;0.0066357
+-83.58769989;34.13314819;0
+-83.6984024;33.32894897;0
+-83.58737183;33.01229858;0
+-84.15579987;33.06710052;0
+-84.13044739;31.76869965;0.018445906
+-81.47805023;31.82209969;0
+-82.43240356;33.8138504;0
+-84.01454926;34.5719986;0
+-83.18945313;34.13605118;0
+-84.68084717;33.0340004;0
+-83.91777802;33.02529907;0
+-83.48120117;33.62604904;0
+-84.76574707;34.78572464;0
+-84.88764954;32.49150085;0
+-83.86270142;33.55619812;0
+-83.46130371;33.83200073;0
+-83.02999878;33.870224;0
+-84.88752747;33.92850113;0
+-84.45574951;34.46844864;0
+-84.38764954;33.08535004;0
+-83.3513031;33.3274231;0
+-85.02404785;31.88125038;0
+-83.38147736;34.85829926;0
+-82.103302;33.38802338;0
+-84.0496521;33.65594864;0
+-84.30594635;32.29282379;0.003728894
+-84.3035965;33.26554871;0
+-83.28205109;34.57320023;0
+-84.85557556;32.07649994;0.003747002
+-84.49479675;32.70069885;0
+-82.84719849;33.58824921;0
+-84.22754669;32.55960083;0.002483838
+-83.74199677;34.89260101;0
+-85.04904938;33.04639816;0
+-83.96894836;34.81779861;0.002179895
+-84.32814789;32.84154892;0
+-85.29270172;34.7859993;0.002099288
+-83.74424744;33.76340103;0
+-82.61795044;33.43090057;0
+-84.98329926;34.80187607;0
+-82.71882629;33.79199982;0
+-83.17667389;32.79354858;0
+-116.5115509;44.85634995;0
+-112.3122025;42.65399933;0
+-111.3354263;42.29932404;0
+-116.6811523;47.21855164;0
+-112.2967758;43.24494934;0
+-113.9973526;43.30445099;0
+-115.631073;43.96549988;0
+-116.5436478;48.36882401;0
+-111.7811966;43.32324982;0
+-116.5395966;48.75045013;0
+-113.2460785;43.7594986;0
+-114.7296982;43.52845001;0
+-111.5887527;42.71924973;0
+-113.6428223;42.33137512;0.000121764
+-112.3051758;44.27024841;0
+-115.524353;46.60914993;0
+-114.3069;44.22200012;0
+-111.8162537;42.21414948;0.000469621
+-111.6010971;44.31740189;0
+-115.5579529;45.88764954;0
+-112.1607513;43.84090042;0
+-116.6823502;47.67882538;0
+-116.683548;46.83604813;0
+-113.8152771;44.96360016;0
+-116.3767471;46.23300171;0
+-111.6871262;43.77560043;0
+-116.7057724;46.24380112;0
+-112.534874;42.25019836;0
+-112.8106232;42.71947479;0
+-116.0068588;47.49879837;0
+-111.2220001;43.7425499;0
+-115.4530258;44.67570114;0
+-116.7665253;44.4958992;0
+-86.2302475;39.19625092;0
+-86.46495056;38.27125168;0
+-85.90899658;38.2995491;0
+-84.99445343;38.96592331;0
+-84.99389648;38.81029892;0
+-95.62460327;43.08375168;0
+-98.67932129;37.23460007;0.002342145
+-96.24465179;37.15069962;0.002191479
+-96.24302673;37.45322418;0.010138423
+-98.20494843;38.69647598;0.002182537
+-96.67514801;39.30485153;0.00307604
+-98.76274872;38.91507339;0.001589558
+-94.75497437;39.09635162;0.025802563
+-84.98335266;38.00844955;0
+-83.69972229;36.76910019;0
+-82.69945526;38.3716011;0
+-83.26405334;37.51699829;0
+-84.36910248;38.95740128;0.006186077
+-83.05590057;38.3356514;0
+-83.0842514;38.12950134;0
+-82.75444794;37.51815033;0
+-84.63105011;38.63814926;0
+-82.91605377;38.5619545;0.005417353
+-83.1901474;36.8430481;0
+-83.99437714;37.41590118;0
+-82.80860138;37.85820007;0
+-84.52079773;38.94240189;0
+-82.91854858;37.34930038;0
+-82.75585175;38.07389832;0
+-83.71430206;37.60537338;0
+-83.36765289;37.10015106;0
+-82.84580231;37.11657333;0
+-84.50274658;36.77724838;0
+-83.07565308;37.69049835;0
+-83.60140228;37.93745041;0
+-83.24340057;37.91395187;0
+-84.8275528;38.53454971;0
+-83.70694733;37.3898735;0
+-83.27500153;37.22169876;0
+-82.35050201;37.47040176;0
+-83.81655121;37.82350159;0
+-84.05715179;38.50839996;0
+-83.47904968;37.74335098;0.003506823
+-70.23999786;44.19710159;0
+-68.30476379;46.51744843;0
+-70.36630249;43.85200119;0
+-70.3812027;45.06335068;0
+-68.37825012;44.68455124;0
+-69.75160217;44.41450119;0
+-69.24015045;44.11790085;0
+-69.53555298;44.07429886;0
+-70.65885162;44.56790161;0
+-68.64790344;45.51974869;0
+-69.28512573;45.79195023;0
+-69.87112427;43.91749954;0
+-69.91004944;45.58877563;0
+-69.15135193;44.50154877;0
+-67.52940369;45.04085159;0
+-70.65910339;43.43640137;0
+-70.51000214;41.77000046;0
+-73.22989655;42.39274979;0
+-71.10990143;41.78844833;0
+-70.64524841;41.39184952;0
+-70.92590332;42.64659882;0
+-72.62809753;42.52774811;0
+-72.6013031;42.17060089;0
+-72.63639832;42.3695488;0
+-71.46009827;42.44660187;0
+-70.02153778;41.31867599;0
+-71.20200348;42.1542511;0
+-70.80687714;41.96364975;0
+-71.05549622;42.33945084;0
+-71.89679718;42.36465073;0
+-83.59294891;44.68405151;0
+-86.49079895;46.4245491;0
+-88.33525085;46.67910004;0
+-84.4693985;45.49354935;0
+-84.37090302;46.35115051;0
+-84.61109924;44.68304825;0
+-87.87014771;45.98479843;0
+-89.70404816;46.43149948;0
+-88.60997772;46.85285187;0
+-88.55419922;46.17105103;0
+-85.09044647;44.68489838;0.001671063
+-88.11855316;47.3390007;0
+-85.80259705;43.99100113;0
+-85.55149841;46.49990082;0
+-84.98999786;46.04007339;0
+-86.27400208;43.99729919;0
+-83.3864975;42.65980148;0.005909674
+-86.26554871;43.64350128;0.029488769
+-89.37519836;46.68190002;0
+-84.12934875;44.68185043;0
+-84.61074829;44.33584976;0
+-85.5271759;41.91524887;0
+-83.21105194;42.23984909;0.005851583
+-93.43254852;46.5923996;0
+-95.00789642;47.97489929;0.000561317
+-92.67780304;46.59329987;0.000908082
+-94.28079987;46.87822342;0.002633079
+-90.27330017;47.85549927;0
+-94.92085266;47.10834885;0.01930978
+-93.73735046;47.46205139;0
+-93.75554657;48.27885056;0
+-91.4102478;47.57300186;0
+-94.88539886;48.87535095;0
+-93.10585022;45.00650024;0
+-91.40457153;31.47050095;0
+-90.8214035;31.17402458;0
+-89.27092743;33.32089996;0
+-88.67275238;32.02705002;0
+-90.42919922;31.8675251;0
+-89.5773468;31.61535072;0
+-89.29502869;31.17180061;0
+-90.89505005;31.46637535;0
+-88.63369751;31.21665001;0
+-89.50630188;30.41394997;0
+-89.09137726;30.49440002;0
+-88.63925171;30.52734947;0
+-89.11669922;32.01309967;0
+-91.02687836;31.74169922;0.00571584
+-89.78130341;31.58209991;0
+-89.17234802;31.62895012;0
+-88.63142395;32.75284958;0
+-89.50125122;31.20804977;0
+-88.65190125;32.40110016;0
+-90.10185242;31.54490089;0.006168288
+-89.52410126;32.75394821;0
+-90.49060059;31.52490044;0
+-89.84694672;31.21655083;0
+-89.11689758;32.7541008;0
+-89.11820221;32.40019989;0.00405472
+-88.87924957;33.42557526;0
+-89.59155273;30.73460007;0
+-88.98995209;31.1718502;0
+-90.40382385;31.17480087;0
+-89.03530121;34.22702408;0
+-89.52384949;32.00284958;0
+-89.11319733;30.77894974;0
+-88.2315979;34.72940063;0
+-90.04820251;31.17539978;0
+-88.69644928;31.66360092;0
+-91.34770203;31.18090057;0
+-89.06620026;33.10264969;0
+-92.7440033;38.04862595;0
+-90.94360352;36.95589828;0
+-93.2562027;36.95349884;0
+-91.31465149;37.95434952;0
+-93.01860046;37.65950012;0
+-91.48100281;37.60345078;0
+-92.49884796;36.93235016;0
+-93.31945038;37.93785095;0.005487545
+-91.88782501;36.77724838;0
+-90.65872192;37.5054512;0
+-92.57250214;39.82184982;0
+-91.39714813;36.69260025;0
+-92.44204712;36.65174866;0
+-91.77700043;37.87614822;0
+-93.40264893;37.62324905;0.004168628
+-92.21720123;37.8118515;0
+-91.02390289;37.32699966;0
+-91.33719635;37.15325165;0
+-93.11109924;40.20954895;0
+-91.95040131;37.32695007;0
+-94.33724976;37.84989929;0
+-90.44490051;37.11935043;0
+-90.23964691;38.65219879;0
+-112.7058029;45.14937592;0
+-107.481102;45.51874924;0
+-108.8939972;48.36724854;0
+-111.4241028;46.29795074;0
+-104.5386505;45.56755066;0
+-111.3413239;47.26119995;0
+-110.470047;47.86149979;0
+-105.5618515;48.78144836;0
+-113.0370026;46.01335144;0
+-104.4747467;46.2826004;0
+-109.2624283;47.24762344;0
+-113.9546967;48.29869843;0
+-111.2937469;45.3396492;0
+-107.0269012;47.40224838;0
+-113.1252975;48.65439987;0
+-109.2164993;46.39837646;0
+-113.4565735;46.38555145;0
+-110.1094742;48.56634903;0
+-112.1176529;46.15890121;0
+-110.2654037;47.0522995;0
+-112.3191528;47.18754959;0
+-111.0752487;48.56534958;0
+-115.3313522;48.45257568;0
+-105.8076248;47.59537506;0
+-110.9649277;46.63619995;0
+-115.0840988;47.09685135;0
+-114.0493011;47.11702347;0
+-108.3947983;46.44437408;0
+-110.4215469;45.59757614;0
+-108.2809982;47.17174911;0
+-108.0382996;48.22529984;0
+-112.3781509;48.23410034;0
+-105.6296997;45.39112473;0
+-112.8842239;46.93304825;0
+-114.0395966;46.08055115;0
+-115.1170502;47.69347382;0
+-104.5515976;48.69464874;0
+-112.6369476;45.9021492;0
+-109.8569489;45.69857407;0
+-112.2117996;47.81774902;0
+-111.7310028;48.60924911;0
+-106.6090469;48.33100128;0
+-109.8346481;46.48582458;0
+-104.3249512;47.01900101;0
+-101.746048;41.91927338;0
+-101.1335983;41.91630173;0
+-100.5554276;41.91415024;0
+-114.9714966;35.92777634;0
+-119.6593475;38.82424927;0
+-115.5318985;41.05950165;0
+-117.7955475;37.7228508;0
+-116.2046967;40.08095169;0
+-118.1675262;41.26435089;0
+-117.191597;40.04764938;0
+-114.9716492;37.75970078;0
+-119.2242966;39.06900024;0
+-118.4232483;38.48384857;0
+-116.5979233;37.58380127;0
+-118.3183517;40.48004913;0
+-119.4953003;39.43915176;0
+-119.5870514;40.58157349;0
+-114.9759521;39.40295029;0
+-119.7768021;39.16794968;0
+-71.44470215;43.52289963;0
+-71.26374817;43.88399887;0
+-72.23245239;42.94684982;0
+-71.38804626;44.69580078;0
+-71.83995056;43.96749878;0
+-71.6536026;42.95195007;0
+-71.6480484;43.31024933;0
+-71.07785034;43.00579834;0
+-71.03430176;43.32672501;0
+-72.19429779;43.36460114;0
+-74.64785004;39.51102448;0.001392429
+-74.08364868;40.95287323;0
+-74.93859863;39.80039978;0
+-74.75279999;39.12770081;0
+-74.24375153;40.79185104;0
+-74.07552338;40.72880173;0
+-74.28109741;40.27539825;0.008608086
+-74.29260254;39.83647537;0.000982272
+-74.30960083;41.01110077;0
+-74.29985046;40.66579819;0
+-106.672699;35.0442009;0
+-108.3785019;33.89094925;0
+-108.0557404;34.96300125;0
+-104.6909485;36.60699844;0
+-104.4189529;34.38734818;0
+-108.3271484;32.53644943;0
+-104.7184525;34.78177643;0
+-103.8998489;35.80390167;0
+-108.6283035;32.05485153;0.001133682
+-105.6285477;33.73934937;0
+-106.2854996;35.86100006;0
+-107.7626266;32.1943512;0
+-105.028923;36.01707458;0
+-105.6120224;32.69540024;0
+-103.5849991;35.17235184;0
+-106.5782013;36.46559906;0
+-104.6791534;35.45624924;0.000168494
+-106.9350967;35.71730042;0.003627118
+-108.2326736;36.50025177;0
+-105.9814987;35.52114868;0
+-107.1699982;33.04285049;0
+-106.8250504;34.02774811;0
+-105.6388016;36.50445175;0
+-106.8076477;34.69778061;0
+-73.84854889;40.85172653;0
+-73.8221283;44.14559937;0
+-74.15924835;42.28039932;0.002412196
+-74.45729828;43.66744995;0
+-73.94519806;40.65179825;0
+-73.60234833;40.75260162;0
+-73.97149658;40.78070068;0
+-73.75704956;41.42414856;0
+-73.83159637;40.67869949;0
+-74.15164948;40.57379913;0
+-74.06137848;41.16104889;0
+-73.82679749;43.52109909;0
+-73.73255157;41.12120056;0
+-81.1276474;36.46794891;0
+-81.48797607;36.41405106;0
+-81.91535187;36.09894943;0
+-82.5298996;35.62105179;0
+-83.74434662;35.07377625;0
+-75.74755096;35.71232605;0.000814693
+-78.85949707;36.05110168;0
+-81.20539856;35.28435135;0
+-83.81204987;35.34825134;0
+-83.00045013;35.54124832;0
+-83.13504791;35.25844955;0
+-82.05055237;35.74354935;0
+-83.42340088;35.16220093;0
+-80.80020142;35.25975037;0.00059379
+-82.12619019;35.99034882;0
+-79.89865112;35.32704926;0
+-77.97650146;35.96649933;0.007519388
+-77.87284851;34.1515007;0.003927261
+-77.88760376;34.5081749;0.019561085
+-82.16300201;35.29359818;0
+-79.76750183;34.99425125;0.001813104
+-81.97910309;35.39545059;0
+-83.56559753;35.48730087;0
+-78.40100098;36.3593483;0
+-81.68689728;36.25114822;0
+-82.31954956;35.88845062;0
+-102.4799728;48.77280045;0.000991138
+-101.5606537;46.37060165;0
+-102.377449;48.14899826;0.000771
+-100.8879623;46.18597412;0
+-80.96994781;40.01134872;0.002907788
+-81.67279816;41.4531517;0
+-80.77490234;40.37685013;0.005721906
+-81.24569702;41.71155167;0
+-82.55180359;38.63124847;0.004121241
+-81.07044983;39.7045517;0.003429995
+-81.46595001;39.76839828;0.005090852
+-94.64015198;35.89994812;0
+-98.32415009;36.73099899;0.000721168
+-96.03939819;34.4184761;0.00063887
+-99.68015289;35.26914978;0
+-98.42142487;35.85829926;0.000673179
+-97.9917984;35.52555084;0
+-97.24759674;34.28889847;0
+-95.03072357;35.89994812;0
+-96.30255127;34.59289932;0
+-98.39994812;34.28889847;0.000736287
+-96.32595062;35.90117645;0
+-94.78714752;36.41590118;0
+-99.00630188;35.98849869;0.000468689
+-97.78244781;36.37919998;0
+-99.56729889;34.92077637;0
+-99.46424866;34.57804871;0
+-97.83760071;34.06980133;0
+-96.66999817;34.31019974;0
+-99.01010132;34.85587311;0
+-95.22112274;34.86944962;0
+-94.74639893;34.94704819;0
+-97.24770355;33.89424896;0
+-94.80870056;34.06147385;0.001684137
+-97.08944702;34.48467636;0
+-96.30284882;35.46440125;0.003001152
+-96.52845001;36.58005142;0
+-96.6371994;36.35957336;0
+-95.72045135;34.94704819;0.000837519
+-96.66934967;34.73474884;0.00086849
+-95.35800171;34.41880035;0
+-99.68190002;35.71720123;0
+-96.6086731;35.1711998;0
+-97.85189819;34.48540115;0
+-96.02950287;36.13970184;0
+-98.99445343;36.69649887;0
+-99.2805481;36.4836998;0
+-117.6473007;44.66842651;0
+-123.4700012;44.49855042;0
+-122.2734756;45.17369843;0
+-123.6898727;46.02154922;0
+-123.0618515;45.94512558;0
+-124.0898514;43.13909912;0
+-120.3807983;44.13087463;0
+-124.1501465;42.47510147;0
+-121.4049606;44.0019989;0
+-123.1011734;43.32374954;0
+-120.2055511;45.43894958;0.000908377
+-118.947525;44.47832489;0
+-119.0666275;43.01990128;0
+-121.6801529;45.49224854;0
+-122.7553024;42.49987411;0
+-121.105751;44.60797501;0
+-123.6135254;42.38954926;0
+-121.5835037;42.80512619;0
+-120.3543472;42.80550003;0
+-122.9735031;43.86312485;0
+-122.5245972;44.49914932;0.000340833
+-122.8246231;44.97919846;0
+-122.0274048;45.58084869;0
+-123.3960037;44.89825058;0
+-120.6918716;45.41025162;0
+-123.6558533;45.41415024;0
+-117.9807968;45.40882492;0
+-117.22435;45.5401001;0
+-121.0860977;45.26345062;0
+-123.1134033;45.54875183;0
+-120.0722504;44.68785095;0
+-123.324501;45.25462341;0
+-75.4041214;39.93379974;0
+-75.35624695;40.21194839;0.020051954
+-75.12809753;40.00175095;0
+-71.28504944;41.71282578;0
+-71.5582962;41.68199921;0
+-71.26744843;41.56900024;0
+-71.55899811;41.87155151;0
+-71.63842773;41.47809982;0
+-82.49060059;34.24890137;0
+-80.71829987;32.39260101;0
+-79.85972595;32.85414886;0
+-81.62072754;35.01129913;0
+-81.17700195;34.68239975;0.001345776
+-81.09752655;34.38774872;0
+-82.45475006;34.84095001;0
+-82.10234833;34.18164825;0
+-81.05370331;32.39944839;0.002143282
+-80.65799713;34.76725006;0.001565375
+-81.9789505;34.49404907;0
+-83.07460022;34.76404953;0.001124262
+-82.71105194;34.86605072;0
+-81.63582611;34.69507599;0
+-103.4024734;43.66695023;0.000200618
+-103.5274734;43.23947525;0.000898061
+-103.4925766;45.57907486;0
+-101.6011276;43.69165039;0.00016719
+-103.7537537;44.37267685;0
+-100.5313721;44.47392654;0
+-84.1914978;36.09024811;0
+-84.13929749;36.38650131;0
+-82.13665009;36.30554962;0
+-85.54489899;36.5149498;0
+-83.10250092;35.94794846;0
+-86.7854538;36.1868515;0
+-85.21019745;35.22100067;0
+-83.15135193;36.49634933;0
+-87.74729919;36.27285004;0
+-85.66275024;36.36687469;0
+-83.96837616;35.99570084;0
+-87.49620056;35.52880096;0
+-84.3585968;35.75379944;0
+-84.625;36.14165115;0
+-85.2901001;36.33819962;0
+-85.152771;36.51677322;0
+-84.52424622;35.84654999;0
+-84.51734924;36.38335037;0
+-83.52435303;35.80052567;0
+-82.41862488;36.11505127;0
+-83.83644867;36.2969017;0
+-85.42814636;35.67504883;0
+-95.66259766;31.79409981;0
+-102.6375504;32.30502319;0
+-94.56614685;31.27625084;0
+-97.02494812;28.07519913;0
+-98.68710327;33.61560059;0
+-101.3571243;34.9654007;0
+-102.8309021;34.0686264;0.007182257
+-99.19094849;29.7622757;0
+-99.21405029;33.61572647;0
+-98.35829926;30.21980095;0
+-101.4319;32.74485016;0
+-97.64064789;31.89565086;0.002053469
+-95.46459961;29.20622444;0.002817549
+-103.0609512;29.81615067;0
+-101.2077026;34.53020096;0
+-98.2539978;27.02280045;0
+-98.93454742;31.76989937;0
+-98.14147186;30.73040009;0
+-99.37227631;32.29724884;0
+-94.93579865;32.98905182;0.00949319
+-94.34822845;33.08644867;0
+-94.66664886;29.70770073;0.002346448
+-95.16365051;31.78204918;0
+-100.207222;34.53002167;0
+-98.20024872;33.80965042;0
+-102.8248978;33.60649872;0
+-100.5299988;31.88949966;0
+-99.45880127;31.76062584;0
+-100.2699509;34.96500015;0
+-98.32219696;29.81595039;0.003617864
+-98.5401535;31.97444916;0.000833164
+-99.85814667;31.33349991;0
+-100.2575989;34.07462311;0
+-102.5338287;31.36879921;0
+-101.6750488;30.68704987;0
+-101.3006516;33.61437607;0.000868528
+-104.4718018;31.33200073;0
+-96.7772522;32.76689911;0.007638415
+-101.9468994;32.74212646;0
+-97.11599731;33.20745087;0.031015411
+-97.36564636;29.09869957;0.007385711
+-100.7787781;33.61634827;0
+-99.7559967;28.42194939;0.001408811
+-98.51730347;27.65992546;0.000697268
+-98.79644775;32.29650116;0
+-102.5426483;31.86902428;0
+-100.2270966;29.95605087;0
+-96.73482513;32.30049896;0.003855702
+-98.2039032;32.21495056;0.000287666
+-100.4012985;32.74185181;0
+-99.76119995;33.98775101;0
+-95.21682739;33.17570114;0
+-96.14179993;31.71199989;0
+-102.6334;32.74095154;0
+-94.97724915;29.31710052;0
+-101.2976227;33.17910004;0
+-98.94550323;30.31679916;0.001914359
+-101.5197983;31.86919975;0
+-94.78237152;32.51554871;0
+-95.98842621;30.54439926;0.003150202
+-100.680603;34.53020096;0
+-98.11454773;31.71705055;0
+-99.7364502;34.32125092;0
+-94.40094757;30.31979942;0
+-94.37245178;32.5617981;0
+-99.73075104;33.17699814;0
+-98.00279999;30.05425072;0.007837048
+-100.2701721;35.83750153;0
+-95.9408493;32.18235016;0
+-102.3449478;33.60672379;0
+-97.84182739;32.39775085;0
+-95.58544922;33.16884995;0
+-101.4341965;32.30590057;0
+-105.4522018;31.31575012;0
+-100.9813004;31.30380058;0
+-98.17230225;33.23455048;0
+-93.99437714;30.69985008;0
+-104.2106018;30.75855064;0
+-94.13500214;29.8641243;0.001210503
+-98.68585205;27.07027435;0
+-99.87879944;32.73675156;0
+-98.6671524;29.92939949;0
+-97.63665009;26.94034958;0
+-100.7776718;33.17869949;0
+-99.33719635;30.03569984;0
+-99.70887756;30.4986496;0
+-100.2545013;33.6162262;0
+-100.4529037;29.35384941;0
+-97.64074707;27.42205048;0.007534703
+-99.73404694;33.61642456;0
+-99.10427856;28.33855057;0.00087776
+-95.58294678;33.66025162;0
+-96.00875092;31.31147575;0
+-98.65679932;30.70375061;0
+-103.6527023;31.82589912;0
+-101.8208466;33.60960007;0
+-101.8158264;33.17734909;0
+-99.34725189;31.21725082;0
+-97.20290375;31.55355072;0
+-98.56867218;28.35415077;0
+-95.92925262;30.95800018;0
+-101.9496002;32.30585098;0
+-99.79969788;30.89900017;0
+-102.03125;31.86910057;0
+-98.6269989;31.47775078;0
+-100.922699;32.30657578;0
+-97.73169708;33.70199966;0
+-95.46330261;30.33665085;0
+-94.73574829;33.1216011;0
+-100.7790985;34.07344818;0
+-94.63574982;31.53520012;0
+-93.73200226;30.7139492;0
+-100.4054031;32.30334854;0
+-102.6020966;35.40530014;0
+-93.90879822;30.05464935;0
+-98.31572723;32.75960159;0
+-94.30705261;32.18294907;0
+-97.80535126;32.7792511;0
+-102.6782532;30.71190071;0
+-94.8690033;30.81780052;0
+-101.8946991;35.401474;0
+-104.3859482;29.94574928;0
+-95.79035187;32.85012436;0
+-101.8955536;34.96530151;0.001537843
+-101.5211029;31.36517525;0
+-99.81414795;29.85284996;0
+-95.02262878;33.63047409;0.012795426
+-103.5560989;31.38145065;0
+-97.1651001;28.30694962;0.030227146
+-100.8125;35.83847427;0.003719876
+-96.40775299;32.89749908;0.031543598
+-99.97470093;31.82970047;0
+-94.7193985;32.12009811;0
+-93.82225037;31.36425018;0
+-94.19139862;31.37594986;0
+-95.10334778;30.62540054;0
+-98.76584625;31.2010994;0
+-100.5386963;30.89690018;0
+-100.9145966;32.7458992;0
+-99.35377502;32.73574829;0
+-94.17275238;31.77395058;0
+-95.28959656;32.41109848;0
+-97.78005219;32.20289993;0
+-98.74485016;26.5177002;0.000383273
+-98.83550262;32.73592377;0
+-101.0443497;31.82169914;0
+-100.2555237;33.17805099;0
+-100.538002;30.49880028;0
+-97.29105377;32.77119827;0
+-99.89035034;32.30149841;0
+-102.1132736;30.21805;0
+-102.3345718;33.17317581;0
+-99.21140289;33.17774963;0
+-94.96704865;33.18842316;0
+-95.1385498;31.10564995;0
+-94.36170197;30.79224968;0
+-94.92735291;32.71295166;0
+-102.0460968;31.36560059;0
+-101.2298508;29.76515007;0
+-95.76255035;32.59659958;0
+-95.59480286;30.78100014;0.003174805
+-103.1884003;31.47260094;0
+-96.44002533;30.21184921;0.004103608
+-99.50489807;27.7322998;0
+-100.2690506;35.40119934;0
+-97.61370087;26.46224976;0.002356791
+-103.0624008;31.86907578;0
+-97.65180206;33.21250153;0
+-95.40850067;32.7806015;0
+-102.8289261;33.17347336;0
+-98.68710327;33.17532349;0
+-99.20379639;26.94470024;0
+-99.76074982;28.86569977;0.005294652
+-109.5239716;40.8273735;0
+-111.3074036;37.84489822;0
+-109.6030502;38.99879837;0
+-111.7737503;37.27214813;0
+-111.5625;41.08477402;0
+-112.1426468;38.32884979;0
+-111.2778015;41.56964874;0
+-111.9059525;40.67369843;0
+-110.2266464;37.74909973;0
+-110.8199997;40.91557312;0
+-113.1057739;40.49005127;0
+-111.2419968;40.28915024;0
+-113.4753494;37.30905151;0
+-110.8664474;38.3301506;0
+-73.08959961;44.02835083;0
+-73.05139923;43.02674866;0
+-72.13600159;44.4632988;0
+-73.0828476;44.44200134;0
+-71.73535156;44.68090057;0
+-72.89167786;44.82440186;0
+-73.2901535;44.78850174;0
+-72.64649963;44.5998497;0
+-72.41480255;43.99610138;0
+-72.22814941;44.77590179;0
+-73.06140137;43.57500076;0
+-72.58535004;44.25944901;0
+-72.72045135;42.99470139;0
+-72.59455109;43.59189987;0
+-79.96520233;37.78377533;0.001757079
+-79.15254974;37.59894943;0
+-77.10894775;38.88755035;0
+-79.7492981;38.0522995;0
+-82.02719879;37.28860092;0
+-80.23674774;37.48815155;0
+-82.35150146;37.12590027;0
+-77.20651245;38.83769989;0
+-80.71555328;37.30687332;0
+-78.47525024;38.3301506;0
+-79.75827026;36.69889832;0
+-79.55404663;38.38589859;0
+-78.90809631;37.79470062;0
+-79.87618256;37.26774979;0
+-82.09384918;36.92584991;0
+-78.19975281;38.89759827;0.005118156
+-82.58850098;37.00125122;0
+-76.56755066;37.23744965;0
+-77.20651245;38.83769989;0
+-79.87618256;37.26774979;0
+-117.1966019;46.22919846;0
+-119.4205017;46.28205109;0
+-120.5125732;47.89955139;0
+-123.8416519;48.13022614;0
+-117.9219513;46.31209946;0
+-122.7273026;46.11940002;0
+-119.6288757;47.66785049;0
+-118.4929276;48.43717575;0
+-117.5451508;46.34407425;0
+-123.7559509;47.16247559;0
+-122.6033173;48.16009903;0
+-123.6296005;47.82139969;0
+-122.7356262;47.63914871;0
+-120.682724;47.16682434;0
+-120.7379532;45.82595062;0
+-122.3979034;46.58914948;0
+-118.3983536;47.60914993;0
+-123.1514282;47.3451004;0
+-119.8595505;48.47202682;0
+-123.7262497;46.5177002;0
+-117.3296967;48.52299881;0
+-122.1117477;47.06850052;0
+-122.9624481;48.56072617;0
+-121.8820038;45.96865082;0
+-117.4302521;47.65370178;0
+-117.9016724;48.41287613;0
+-122.7014008;46.97845078;0
+-123.4692001;46.26615143;0
+-118.5158005;46.30067444;0
+-117.6288528;46.84270096;0
+-80.01875305;39.12319946;0
+-81.71309662;37.99900055;0
+-80.74144745;38.70555115;0
+-80.5997467;40.27980042;0
+-82.27365112;38.41384888;0
+-81.13179779;38.82585144;0
+-81.05110168;38.46554947;0
+-80.72109985;39.2730484;0
+-81.06500244;38.04114914;0
+-80.81994629;38.91350174;0
+-79.23370361;39.07049942;0
+-80.4213028;37.97660065;0
+-78.6638031;39.31389999;0
+-80.59349823;40.51670074;0
+-80.38150024;39.28509903;0
+-81.70680237;38.82645035;0
+-81.55484772;38.29778671;0
+-80.51464844;38.94725037;0
+-82.05470276;38.15930176;0
+-81.89914703;37.83480072;0
+-80.21679688;39.51160049;0
+-80.69047546;39.8769989;0
+-81.10590363;37.41360092;0
+-78.96514893;39.43315125;0
+-82.10825348;37.74335098;0
+-80.09324646;39.57844925;0
+-80.53994751;37.55189896;0.002640641
+-78.24555206;39.53824997;0
+-80.83409882;38.30270004;0
+-80.62550354;40.10039902;0
+-79.35340118;38.68460083;0.002462851
+-81.18979645;39.36285019;0
+-79.99420166;38.3818512;0
+-81.88064575;38.47592545;0
+-81.24150085;37.74874878;0
+-79.81510162;38.75194931;0
+-81.0556488;39.1922493;0
+-81.31529999;38.72014999;0
+-80.87750244;37.64844894;0
+-80.04850006;39.34524918;0
+-79.57324982;39.11780167;0
+-80.87020111;39.45055008;0
+-80.22667694;38.90465164;0
+-82.43460083;38.13857651;0
+-80.43090057;38.48244858;0
+-80.66999817;39.57524872;0
+-81.37197876;39.04090118;0
+-81.49810028;39.20055008;0
+-81.53919983;37.60660172;0
+-90.61440277;46.34104919;0
+-91.16145325;46.55722427;0
+-91.92195129;46.4565506;0
+-88.37110138;45.86370087;0
+-88.73664856;45.72534943;0
+-90.23970032;46.28457642;0
+-89.5454483;45.68199921;0
+-89.48815155;46.07839966;0
+-105.7952728;41.74229813;0
+-105.5462265;44.24772644;0
+-106.9998779;41.71694946;0
+-105.4845505;42.89442444;0
+-104.5715027;44.58840179;0
+-108.7768478;43.13479996;0
+-108.4534988;43.76967621;0
+-106.6884537;44.02774811;0
+-110.5459976;42.44677353;0
+-106.8064499;42.96617508;0
+-104.4781494;43.0461998;0.000297527
+-106.9599991;44.77902603;0
+-109.8338776;42.8641243;0
+-108.77845;41.63399887;0
+-110.5525513;43.95159912;0
+-110.5467758;41.2873497;0
+-104.5677032;43.83959961;0
+-79.51689911;37.32115173;0.136066763
+-79.91410065;37.00320053;0.355538922
+-96.67050171;44.02225113;0.306985683
+-97.75624847;43.33387375;0.272584951
+-98.14459991;43.67520142;0.183004547
+-97.490448;44.36967468;0.159535766
+-76.29844666;40.01839828;0.117221251
+-76.41539764;40.37530136;0.111835378
+-77.7766037;40.00479889;0.042305226
+-78.22190094;42.69475174;0.027407195
+-94.05590057;45.6916008;0.201684506
+-100.0701523;45.42024994;0.046137028
+-100.8709259;37.56217575;0.296603564
+-74.42404938;42.91009903;0.037439479
+-75.62664795;38.39447403;0.047747413
+-78.18515015;42.9978981;0.071783105
+-94.20845032;46.06135178;0.104351934
+-87.57039642;44.5019989;0.091659614
+-94.59339905;45.52904892;0.1900074
+-97.71869659;47.4557991;0.066241086
+-114.839447;42.9239006;0.008980131
+-81.88874817;40.82934952;0.12829516
+-81.93495178;40.55614853;0.090132154
+-88.0075531;44.46030045;0.091915242
+-120.9292984;37.60620117;0.001882795
+-73.44345093;43.37464905;0.01402704
+-103.8069992;40.26269913;0.079014829
+-97.78714752;43.67490005;0.247981645
+-77.08255005;40.97790146;0.057225397
+-76.50295258;43.01860046;0.082049396
+-75.88829803;39.54389954;0.107499499
+-88.22290039;44.06784821;0.141183873
+-77.23970032;40.13660049;0.054244597
+-89.42377472;43.07009888;0.21658596
+-120.6414032;37.18704987;0.006157029
+-87.78025055;44.10962677;0.100375298
+-79.20079803;39.46244812;0.052092762
+-78.85545349;38.52819824;0.016521765
+-114.2736969;42.67364883;0.002865347
+-77.07855225;40.76377487;0.064160628
+-75.61250305;40.60295105;0.144684273
+-86.02729797;42.98675156;0.115188279
+-97.4878006;27.77702522;0.046361657
+-77.87619781;39.31275177;0.078300243
+-102.3577499;40.59415054;0.149973823
+-91.68160248;44.01990128;0.17788001
+-121.2508011;37.89110184;0.052482348
+-94.89949799;46.0713501;0.095871107
+-98.09037781;44.02327347;0.169428042
+-78.76550293;43.19697571;0.076190362
+-97.60175323;30.65670013;0.153359548
+-78.36840057;40.49295044;0.01812714
+-75.33239746;40.75249863;0.120385233
+-76.19155121;43.01224899;0.051062746
+-119.8937988;36.13874817;0.003931552
+-75.74894714;39.98160172;0.0607663
+-87.92772675;43.36762619;0.08218366
+-88.52249908;43.74034882;0.168589451
+-88.22924805;43.36782455;0.095384964
+-92.47022247;44.7011261;0.183220871
+-90.6187973;44.72795105;0.070693215
+-83.03820038;43.87039948;0.184917874
+-118.7862473;36.26660156;0.00097171
+-77.58384705;40.61085129;0.043628296
+-91.1140976;45.89749908;0.072828316
+-88.7045517;43.41410065;0.218007605
+-76.52144623;41.77199936;0.013443255
+-80.90254974;35.78229904;0.008144101
+-98.56402588;43.71910095;0.061116171
+-96.56945038;33.19329834;0.065076514
+-82.501297;36.26679993;0.010055622
+-75.98529816;40.40707397;0.070404189
+-116.7490997;43.58549881;0.038418656
+-94.94725037;46.5870018;0.051399948
+-80.66020203;36.16659927;0.037252272
+-88.58339691;39.06354904;0.308778985
+-75.61994934;42.95365143;0.025729576
+-98.01625061;27.65914917;0.039581213
+-76.06954956;42.59925079;0.013446913
+-99.34745026;39.7846489;0.016044086
+-88.05265045;38.41289902;0.274763886
+-77.13542175;42.61175156;0.04665696
+-75.91560364;44.03570175;0.013387243
+-100.4766464;40.17657471;0.084376962
+-77.92549896;38.5033989;0.025426509
+-79.41112518;42.28424835;0.013535144
+-93.59417725;44.67630005;0.168656078
+-75.48214722;43.23970032;0.024093964
+-93.28585052;45.94509888;0.043160517
+-93.43184662;39.41094971;0.170482353
+-76.32620239;42.20422363;0.006928512
+-88.6446991;44.06855011;0.146067992
+-77.50330353;40.47864914;0.035113256
+-77.96724701;40.40240097;0.015195069
+-73.64150238;42.2439003;0.015484168
+-96.96547699;30.78384972;0.087906089
+-79.91410065;37.00320053;0.004289605
+-78.12239838;39.9435997;0.013926343
+-88.73345184;44.80720139;0.104951522
+-85.09377289;44.33705139;0.01681738
+-90.13334656;43.00954819;0.115957754
+-76.47154999;42.44490051;0.027898855
+-97.10614777;31.9877491;0.059424228
+-79.03829956;40.0033493;0.02079146
+-75.47944641;43.81880188;0.004900347
+-88.05419922;42.58107376;0.184409706
+-74.98329926;40.84315109;0.077716769
+-92.8830719;45.02140045;0.088556293
+-97.52014923;28.0115242;0.038630449
+-97.6101532;44.02199936;0.150677666
+-92.52014923;45.46887589;0.08977046
+-77.35749817;42.28955078;0.019073486
+-75.02324677;42.61135101;0.008569755
+-101.7837524;37.5632515;0.133501838
+-78.03645325;38.25094986;0.026979075
+-101.0414734;40.17652512;0.053694914
+-75.59320068;42.4701004;0.006638826
+-103.3908005;34.62827682;0.007223763
+-94.80390167;30.19034958;0.019671375
+-92.08083344;44.54589844;0.152698252
+-90.78974915;43.57695007;0.095731063
+-104.3121033;40.50102615;0.027786823
+-90.38230133;39.31999969;0.381497542
+-97.73245239;28.40997505;0.040654044
+-73.68309784;44.72014999;0.00676765
+-91.84752655;45.4233017;0.109014997
+-78.79982758;42.76480103;0.009875887
+-98.62909698;44.0658493;0.050421298
+-116.7169037;43.97634888;0.018787421
+-81.06005096;36.91790009;0.001686491
+-95.7568512;29.52535057;0.031798797
+-86.09747314;35.17549896;0.064808303
+-76.86329651;42.39025116;0.107722138
+-74.0583725;42.83314896;0.011555095
+-81.36775208;40.81159973;0.067800361
+-77.99024963;39.12194824;0.024770694
+-91.47405243;43.6739769;0.139066725
+-90.64234924;43.94329834;0.077537829
+-106.3127975;31.69482422;0.057851284
+-77.06900024;37.70434952;0.059565951
+-102.6047974;39.30607605;0.061824057
+-90.43157196;43.35945129;0.072234306
+-82.53215027;40.77280045;0.088992815
+-78.68492126;42.26824951;0.006799443
+-99.89189911;37.69052505;0.062016386
+-89.76987457;44.90084839;0.063209565
+-78.27410126;38.43054962;0.022847831
+-85.55059814;43.03134918;0.065882068
+-81.88569641;31.57670021;0.015512053
+-76.77980042;42.78255081;0.001803601
+-74.35434723;41.38750076;0.004593674
+-76.77915192;40.38904953;0.043428392
+-92.45302582;45.03414917;0.12703252
+-82.50330353;35.32265091;0.007937834
+-93.62020111;45.90295029;0.051678068
+-76.00225067;41.51464844;0.010227845
+-93.88839722;45.20019913;0.16340333
+-119.78125;37.27455139;0.00409671
+-99.10482788;43.71709824;0.014690888
+-85.80059814;43.5542984;0.035057338
+-88.46472168;44.41635132;0.134694069
+-91.16725159;43.90245056;0.100775279
+-81.2023468;36.21879959;0.002476261
+-97.49185181;31.03610039;0.104538227
+-77.759552;40.97219849;0.023842469
+-93.82922363;45.40304947;0.106721293
+-75.74472809;42.20664978;0.004863332
+-75.19329834;44.52740097;0.007313633
+-76.00350189;39.23550034;0.044635772
+-87.97914886;42.3240509;0.045384976
+-121.9621506;38.61965179;0.028836095
+-85.28504944;37.11790085;0.011137671
+-77.28659821;42.80547333;0.062802616
+-77.24289703;41.7717514;0.008544803
+-81.17359924;35.9123497;0.002402183
+-84.86039734;37.82440186;0.013699586
+-76.32319641;39.51015091;0.010851364
+-77.8309021;38.71165085;0.016834693
+-101.3326721;47.13880157;0.006262785
+-77.21365356;39.89474869;0.062799273
+-100.4394989;37.73867416;0.138824951
+-93.98370361;39.65705109;0.07424186
+-80.72145081;37.06190109;0
+-80.06504822;42.05924988;0.038772679
+-73.52604675;42.70397568;0.015291455
+-78.47174835;40.02460098;0.017557737
+-99.01667786;43.24895096;0.030298593
+-89.53410339;44.46474838;0.070924954
+-78.01644897;42.25987625;0.006218332
+-97.9720993;29.61750031;0.050535056
+-83.71700287;38.35639954;0.011133993
+-81.54940033;36.82624817;0
+-75.85945129;38.88904953;0.03848701
+-84.21035004;38.21929932;0.018229077
+-83.00710297;30.15925026;0.005226208
+-121.4436493;38.38034821;0.038833946
+-79.13085175;38.18212509;0.01304284
+-92.71677399;46.07474899;0.016720823
+-121.7280273;48.82182312;0
+-92.45829773;45.8987999;0.024725509
+-75.80505371;41.82020187;0.002658411
+-81.19722748;41.53134918;0.015870411
+-77.45944977;37.77330017;0.039667428
+-79.40384674;36.83570099;0.004506778
+-78.02719879;39.44200134;0.014596438
+-85.95294952;36.94615173;0.026732794
+-80.20490265;33.69269943;0.090063569
+-96.15104675;38.45500183;0.03195259
+-87.89369965;41.46519852;0.219736481
+-81.48870087;40.4334259;0.039641989
+-101.8924484;35.8376503;0.112021486
+-93.76634979;44.80807495;0.251634971
+-101.3077469;37.56207657;0.095403664
+-77.03929901;43.17802429;0.063616127
+-94.83174896;38.89780045;0.029822455
+-75.10424805;40.32844925;0.043990171
+-88.04454803;42.72717667;0.184347793
+-97.7710495;30.3240509;0.023217174
+-74.959198;43.46089935;0.004982285
+-88.30259705;43.0190506;0.084933763
+-83.27459717;36.1875;0.014554741
+-84.6006012;42.9442482;0.155577381
+-95.28250122;38.56432343;0.068343208
+-79.45165253;40.85025024;0.025088695
+-77.9457016;37.34529877;0.008758408
+-78.70259857;40.484375;0.020439086
+-116.2430496;43.46039963;0.002517796
+-84.23077393;30.87730026;0.050478041
+-90.79149628;42.85800171;0.170291197
+-73.97104645;42.60485077;0.004477505
+-89.73590088;45.33752441;0.006368165
+-97.65032196;39.82785034;0.076338675
+-84.12680054;44.33485031;0.021597554
+-85.70760345;36.72847366;0.00425093
+-97.08719635;39.78412628;0.019303864
+-79.78990173;36.07835007;0.006496181
+-85.05535126;37.00659943;0.019105234
+-85.32575226;43.98934937;0.010490653
+-100.7012024;43.94284821;0.003863908
+-99.8376236;48.77217484;0.010734667
+-84.84604645;37.62459946;0.006871908
+-77.29364777;40.40274811;0.02371188
+-85.44764709;35.93529892;0.004979022
+-91.29419708;45.07412338;0.096184298
+-80.80290222;41.0170517;0.044024684
+-101.5264969;44.37115097;0.001551322
+-89.38339996;43.81245041;0.091590924
+-82.29940033;31.71859932;0.008603567
+-86.1256485;43.2947998;0.03345233
+-77.76415253;42.72977448;0.066244285
+-81.23925018;35.48495102;0.00418354
+-81.96920013;36.76150131;0
+-102.3358994;41.11222458;0.049017936
+-83.08580017;39.07284927;0.02406623
+-79.45414734;41.20249939;0.019452145
+-84.87174988;34.50704956;0.021094673
+-79.92774963;40.9211998;0.028335954
+-91.7963028;45.89787674;0.018139882
+-91.80619812;44.31100082;0.116193916
+-97.6053772;29.85680008;0.023764041
+-83.45400238;36.03620148;0.006847814
+-97.88240051;28.94459915;0.024781093
+-80.39254761;37.17675018;0
+-101.0554962;39.35065079;0.104381827
+-76.65670013;41.02730179;0.064531549
+-111.7887268;41.68495178;0.000805053
+-74.92559814;42.18299866;0.002052552
+-77.61904907;41.22055054;0.010523421
+-75.73629761;40.93024826;0.008201239
+-102.4787521;45.49147415;0.000108824
+-80.54154968;35.34590149;0.012863964
+-76.97104645;38.48952484;0.084379399
+-76.75154877;42.14704895;0.00803893
+-97.36837769;43.67440033;0.13709249
+-73.74365234;41.75970078;0.01150058
+-81.19824982;41.16799927;0.046964804
+-76.88005066;38.83375168;0.035316435
+-78.96320343;41.14044952;0.013348996
+-80.1921463;40.21744919;0.009844357
+-79.8042984;35.71310043;0.008533263
+-76.59004974;34.92375183;0.061617517
+-87.33294678;30.63694954;0.007784781
+-94.9815979;39.19150162;0.053289658
+-96.52449799;29.60485077;0.023852485
+-84.86125183;35.17264938;0.002376643
+-111.9562531;41.24985123;0.001628905
+-103.4955215;34.08747482;0.001276292
+-92.85385132;38.43907547;0.016215832
+-103.115799;40.71889877;0.03654434
+-77.02394867;41.33272552;0.024674049
+-84.54634857;37.6440239;0.006757926
+-80.37850189;36.91579819;0
+-91.28562164;44.72674942;0.093116806
+-74.42894745;43.13579941;0.002519298
+-96.83875275;37.23762512;0.006936799
+-80.33242798;40.66564941;0.012594712
+-112.1154022;40.96110153;0.00564487
+-74.31819916;44.55255127;0.001532342
+-95.63300323;41.03082657;0.3633055
+-92.20449829;44.28115082;0.2202339
+-99.89794922;38.08760071;0.013627721
+-75.27570343;41.61669922;0.00107135
+-85.83920288;31.09630013;0.016539269
+-91.10929871;45.46545029;0.0162559
+-98.07185364;28.42144966;0.010102046
+-79.26439667;41.81162643;0.003006678
+-98.90977478;34.37832642;0.008422021
+-79.41192627;37.80934906;0.00390866
+-95.40799713;35.55997467;0.016319039
+-83.73744965;38.14697647;0.014536964
+-96.58370209;31.51754951;0.022005105
+-74.43840027;42.59230042;0.01426029
+-84.24344635;35.43344879;0.002214881
+-79.78645325;37.55405045;0.004031069
+-77.8927536;37.53839874;0.004186122
+-75.8877182;36.28860092;0.068064578
+-121.7008514;48.47665024;0
+-81.90615082;40.30405045;0.060387009
+-77.90274811;41.73757553;0.00216779
+-83.55075073;30.85754967;0.020575037
+-78.26820374;37.52827454;0
+-76.62065125;39.28445053;0.127115734
+-97.88175201;35.02954865;0.00269651
+-80.74984741;36.74480057;0
+-102.6016235;36.27795029;0.135929161
+-82.96057129;38.79315186;0.020160632
+-82.87174988;36.18180084;0.003769966
+-78.47669983;38.62215042;0.010044033
+-102.4962006;46.11362457;0.006484649
+-88.77455139;43.02017593;0.213191864
+-94.85085297;37.85540009;0.019375206
+-88.35057831;34.27595139;0.013208938
+-76.46295166;37.73007584;0.061025988
+-79.39622498;36.04650116;0.005450708
+-96.90019989;29.3478508;0.009665945
+-98.46344757;29.43320084;0.01478762
+-91.35504913;44.29072571;0.134522123
+-79.51689911;37.32115173;0.001242545
+-78.23104858;43.25234985;0.094612289
+-107.33815;46.17634964;0.003032865
+-79.13379669;37.22645187;0.003097249
+-84.02124786;38.32574844;0.007947205
+-77.46252441;38.7228508;0.004625792
+-81.09140015;40.57690048;0.025733524
+-90.02112579;44.46644974;0.047696082
+-83.74930573;41.57374954;0.119782794
+-94.07710266;46.48080063;0.009093691
+-94.845047;38.2132988;0.027926487
+-79.10759735;36.0522995;0.002735469
+-76.74880219;37.31034851;0.017492303
+-118.9042511;35.30452347;0.000307089
+-85.10835266;38.46620178;0.014581605
+-85.60929871;37.00025177;0.00698264
+-81.25797272;36.68259811;0
+-86.04634857;35.80860138;0.030586508
+-80.80325317;40.75624847;0.044017391
+-81.15865326;37.12704849;0
+-108.1947479;45.9782486;0.000830124
+-76.51820374;41.43284988;0.003472685
+-85.27565002;37.57115173;0.018793289
+-106.8195038;32.42020035;0
+-81.44544983;39.43090057;0.022386444
+-85.23204803;30.78317451;0.01143326
+-82.33920288;38.80889893;0.006999552
+-81.4776001;40.03079987;0.010179153
+-76.18695068;43.43109894;0.00491713
+-83.92584991;35.67375183;0.006154218
+-119.6395035;36.74649811;0.001310222
+-88.70809937;37.08420181;0.004609354
+-84.74559784;38.02125168;0.012291536
+-96.11552429;33.60689926;0.025415054
+-78.45664978;37.23849869;0
+-77.64550018;39.08599854;0.018034048
+-93.26560211;45.22517395;0.029508274
+-87.55525208;45.54024887;0.006588278
+-79.6522522;39.93080139;0.01680873
+-84.84179688;38.75960159;0.014231859
+-81.54599762;35.37155151;0.001008867
+-114.5181503;42.45629883;0.00267853
+-81.6309967;34.30407333;0.001486279
+-89.03199768;45.24959946;0.0100267
+-82.93714905;32.43135071;0.009425745
+-84.06710052;38.69055176;0.006919841
+-98.20890045;39.04510117;0.00304297
+-78.28874969;39.23690033;0.00753684
+-85.9076004;37.29914856;0.008640276
+-104.4311523;33.3036499;0
+-75.6413269;41.40190125;0.003406886
+-84.57119751;38.3049469;0.010975695
+-85.85765076;41.59764862;0.150250102
+-90.93560028;43.20624924;0.066844628
+-84.57804871;39.44094849;0.095969675
+-81.63259888;37.375;0.013387171
+-79.68840027;39.45800018;0.004579143
+-84.53420258;35.13657379;0.005746466
+-82.98605347;30.47687531;0.014870547
+-87.35189819;44.98690033;0.0362529
+-84.65350342;37.43629837;0.021839499
+-82.25379944;36.50624847;0.003026488
+-81.83350372;39.6100502;0.011597352
+-121.692749;48.03739929;0
+-84.32845306;38.43035126;0.012109274
+-85.81694794;44.96327591;0.013003271
+-100.4411469;39.35010147;0.105112622
+-78.38079834;36.71620178;0.002754716
+-82.03414917;39.03950119;0.009096572
+-102.621727;47.40205002;0.002254802
+-85.13549805;36.74520111;0.007121696
+-95.9924469;29.9864502;0.026466036
+-83.91815186;38.04902649;0.008654766
+-81.23177338;35.68164825;0.006250469
+-114.1533966;42.9822998;0.000388833
+-84.04640198;32.34645081;0.013560677
+-81.10269928;40.29645157;0.008905781
+-84.73789978;38.96234894;0.013324332
+-80.43499756;33.07540131;0.020118142
+-85.71240234;30.60980034;0.003233149
+-111.5237274;40.1894989;0.002111138
+-88.91470337;44.4620018;0.080507995
+-101.6870728;40.17655182;0.098180701
+-95.71320343;46.41239929;0.091711665
+-84.15644836;37.97404861;0.011059337
+-86.66419983;32.5245018;0.005243553
+-75.38645172;38.70584869;0.171485367
+-85.60350037;31.40769958;0.008633058
+-81.56324768;37.1480484;0
+-85.14565277;45.00999832;0.008845452
+-75.30805206;41.03369904;0.009244043
+-84.11979675;37.1406517;0.002869144
+-96.80934906;48.77197647;0.002990894
+-80.29049683;35.32320023;0.039155445
+-76.94000244;39.23384857;0.065288975
+-104.378952;39.86967468;0.005899046
+-77.18630219;37.15679932;0.028235826
+-85.41649628;35.35720062;0.005877374
+-94.83724976;38.56370163;0.047683451
+-96.68297577;43.29190063;0.381104153
+-77.66139984;38.18370056;0.010914035
+-86.8302002;34.4952507;0.015565689
+-87.89395142;41.8118515;0.011235302
+-78.52404785;38.00540161;0.00194607
+-98.21800232;39.7846489;0.01117078
+-96.59715271;38.30405045;0.012888425
+-82.01035309;39.36819839;0.005241633
+-101.8928757;36.27774811;0.120188401
+-76.62754822;38.97434998;0.022539309
+-100.7129517;43.1936264;0.004164806
+-85.23355103;34.33324814;0.009744119
+-82.61599731;39.02614975;0.010409727
+-120.6921234;46.56380081;0.004618875
+-83.43990326;38.20759964;0.006120469
+-89.63282776;35.73509979;0.066425763
+-98.07542419;37.19210052;0.002924424
+-85.46044922;38.40465164;0.029732015
+-96.94329834;29.89590073;0.016940255
+-102.60495;34.96654892;0.014779802
+-102.3503494;34.06869888;0.03885756
+-78.63079834;36.28224945;0.002353517
+-87.94799805;43.01732635;0.008408884
+-83.75;34.65200043;0.004527486
+-112.4899979;33.34999847;3.39558E-05
+-86.91165161;35.87590027;0.009921827
+-79.23135376;35.69549942;0.002517024
+-104.9652252;42.12182617;0.001573794
+-77.99720001;37.93412399;0.006914551
+-104.6652527;41.32749939;0.002268666
+-78.42254639;40.98905182;0.004630356
+-93.79290009;38.38502502;0.048266499
+-82.79190063;35.22520065;0.003097003
+-97.40715027;35.55134964;0.00462741
+-74.60632324;40.56565094;0.00974351
+-105.5664978;40.62805176;0.004504959
+-96.9368515;31.25395012;0.155215905
+-85.81580353;34.52985001;0.026111632
+-87.84677124;35.63195038;0.008661934
+-85.95850372;36.25479889;0.011430139
+-78.01672363;37.14110184;0.002482918
+-84.32044983;37.34540176;0.003936636
+-83.4957962;30.45549965;0.01016385
+-78.13700104;38.69139862;0
+-80.33370209;36.7071991;0.001940431
+-83.69045258;36.47187424;0.002158746
+-101.5401993;48.72917557;0.006251643
+-100.4660034;38.48125076;0.008496347
+-99.32440186;39.35029984;0.008266824
+-105.880249;34.6508255;0.000233571
+-85.45355225;36.13899994;0.001948011
+-78.27774811;37.84820175;0.006524689
+-101.1687775;45.70807648;0.001705972
+-80.2154007;39.86500168;0.001899333
+-84.83830261;42.59614944;0.175656217
+-84.57994843;37.87609863;0.009927511
+-84.53974915;39.16675186;0.013808425
+-81.95794678;32.78025055;0.011166776
+-78.8214035;38.99945068;0.006963609
+-96.97485352;28.80164909;0.063208215
+-86.45257568;35.52477646;0.021441615
+-100.7207718;43.61985016;0.002033119
+-95.79975128;39.43462372;0.056962049
+-99.31725311;38.91479874;0.003472338
+-96.07794952;33.12310028;0.009102014
+-82.45400238;39.5115509;0.009240095
+-80.2661972;33.90570068;0.061052584
+-108.2266006;38.93310165;0.001408609
+-97.13535309;38.87120056;0.004791136
+-74.75759888;41.72069931;0.000644523
+-103.4849472;46.45552444;0.001411194
+-87.79872131;35.24755096;0.003831953
+-93.34349823;37.25794983;0.001388951
+-100.8209;48.77209854;0.004682097
+-90.3967514;32.31105042;0.022111358
+-83.49410248;36.24689865;0.002786894
+-85.31755066;34.43772507;0.006482782
+-100.1956482;44.72272491;0.013810954
+-84.84780121;43.98889923;0.006064749
+-104.6388779;47.75189972;0.001574428
+-111.3255234;32.98355103;0.000261892
+-87.37284851;41.43740082;0.183600748
+-98.7677002;39.35042572;0.004202199
+-78.59185028;38.84844971;0.015252831
+-78.53275299;37.56487656;0.001613986
+-103.7264481;42.50114822;0.00899731
+-91.50505066;38.43109894;0.014404517
+-74.67925262;41.13439941;0.004796071
+-100.8794022;45.10105133;0.001560709
+-111.9076233;38.77389908;0.000245387
+-94.42375183;40.47809982;0.001053394
+-96.25824738;44.0231514;0.314586061
+-78.80844879;37.37889862;0.002809494
+-103.4329987;32.78530121;0
+-85.62404633;35.15100098;0.015297131
+-84.97679901;35.95119858;0.001146183
+-104.5515747;38.1283493;0.001308248
+-87.26834869;37.53490067;0.029320275
+-85.30852509;42.59529877;0.093838782
+-93.47290039;45.01672363;0.042668248
+-84.54354858;32.0761261;0.012676577
+-84.37169647;42.59930038;0.13691679
+-84.87445068;38.23479843;0.007424214
+-79.77320099;36.39187622;0.003861839
+-77.88905334;37.73460007;0.019227051
+-96.30255127;32.59960175;0.006957343
+-95.66374969;35.35169983;0.003810162
+-80.0270462;40.43574905;0.002353904
+-74.72489929;39.84500122;0.01495364
+-84.61180115;45.0284996;0.002733024
+-86.78502655;34.08580017;0.007194318
+-96.63130188;30.51350021;0.030520329
+-97.33634949;30.10289955;0.002638318
+-82.00830078;31.19174957;0.003867574
+-100.4589996;39.7845993;0.01503833
+-81.84909821;36.43915176;0.003141019
+-74.34625244;41.87879944;0.002219303
+-85.20807648;37.77474976;0.014034898
+-74.71309662;40.28089905;0.02351392
+-99.24825287;38.17515182;0.053452802
+-83.85929871;32.58010101;0.013446644
+-74.41435242;40.4300499;0.020684005
+-103.5206985;46.11309814;0.003227056
+-84.82189941;35.51985168;0.004009289
+-84.91402435;45.53020096;0.004007952
+-109.753952;31.87989998;0.005470504
+-121.3218536;39.31169891;0.000990978
+-78.67414856;36.97375107;0.003618497
+-98.74700165;38.04270172;0.089364623
+-81.33634949;32.34859848;0.015644229
+-99.64890289;36.79745102;0.002556545
+-116.293045;44.15359879;0.005277027
+-85.16449738;35.56404877;0.004999139
+-90.36100006;45.67955017;0.002120657
+-93.30290222;35.03034973;0.006735712
+-83.92375183;37.69784927;0.004922616
+-87.04789734;31.4720993;0.003305751
+-78.9747467;36.38935089;0.007169075
+-94.39467621;33.47237396;0.013726672
+-99.29154968;37.91059875;0.140168323
+-84.93579865;37.33295059;0.011220574
+-89.24165344;44.11384964;0.057904573
+-96.98719788;30.29459953;0.002983294
+-103.5065994;44.89274979;0.000764395
+-85.32865143;37.33772659;0.035323166
+-103.2431259;40.00215149;0.012086074
+-77.46085358;38.41225052;0.007513409
+-95.3756485;47.58610153;0.002513295
+-95.43830109;29.8335495;0.004518952
+-105.3452988;40.08834839;0.005261226
+-103.1395493;42.71977615;0.001790523
+-111.6306992;39.42274857;9.83874E-05
+-74.58035278;40.86740112;0.002998465
+-95.51364899;35.97112656;0.013601197
+-75.34355164;38.22299957;0.101910279
+-84.10012817;36.77887344;0.002839941
+-81.46824646;31.98069954;0.002122433
+-79.33517456;36.39179993;0.00202363
+-85.68344879;37.96504974;0.012016016
+-88.00655365;45.38050079;0.019729661
+-97.30004883;34.68099976;0.004837521
+-80.06604767;41.66595078;0.034868967
+-81.34654999;32.95135117;0.042871142
+-85.17205048;33.99862671;0.006528412
+-89.95574951;43.40137482;0.0038552
+-97.64785004;38.39172363;0.002319454
+-87.76805115;36.02930069;0.019082804
+-106.5646362;46.01857376;0.000405247
+-86.0372467;44.64599991;0.003890324
+-86.07479858;35.49639893;0.058296801
+-82.63962555;36.73834991;0
+-112.9580994;41.50037384;0.001255823
+-75.02760315;41.34534836;0.003428979
+-102.5408478;43.35015106;0.000895467
+-99.69192505;36.2181015;0.002288185
+-80.25900269;41.28094864;0.058842026
+-109.4506989;45.65309906;0.000348172
+-81.56974792;35.9416008;0.002153573
+-81.74034882;33.99644852;0.003107872
+-101.339447;46.63344955;0.001054499
+-108.9611511;45.31235123;0.00106812
+-98.99047852;41.91514969;0.022202779
+-82.21060181;31.37512589;0.025036417
+-102.5637741;37.31869888;0.014061595
+-83.72454834;37.14355087;0.002985605
+-97.49434662;29.4470005;0.007024116
+-82.65329742;34.51390076;0.000979255
+-98.20097351;38.34754944;0.029676705
+-79.9055481;33.18830109;0.003558512
+-87.40194702;35.22848892;0.030380608
+-86.42475128;35.8628006;0.011613159
+-86.00875092;36.52600098;0.006584096
+-122.5142517;45.80170059;0
+-87.33679962;31.52842522;0.003503192
+-102.7869034;44.58980179;0.000270124
+-83.84079742;36.87099838;0.003627534
+-88.13710022;32.65155029;0.002762614
+-89.80995178;43.94509888;0.048727113
+-81.01035309;33.23289871;0.022647807
+-91.09055328;39.36830139;0.120295919
+-107.8827515;44.58340073;0.001045987
+-82.24859619;39.74140167;0.067871785
+-95.7696991;39.04302597;0.033533702
+-81.5401001;41.12820053;0.006057375
+-77.8513031;36.78357315;0.001379959
+-78.70075226;39.58987427;0.001469136
+-83.91165161;42.60359955;0.05387931
+-99.97660065;41.91284943;0.002198413
+-84.0122757;35.13964844;0.00205958
+-97.46929932;28.64642525;0.00878714
+-85.52484894;37.29014969;0.01786271
+-105.3423233;46.86125183;0.000539864
+-105.4614487;46.32397461;0.000289112
+-93.2664032;45.57305145;0.112809461
+-75.96070099;41.16464996;0.012804358
+-101.1475525;38.91717529;0.009028509
+-86.14414978;32.58969879;0.005784615
+-99.76184845;29.35695076;0.016964601
+-99.27275085;37.19174957;0.002180297
+-113.212326;38.3614769;0
+-81.96604919;34.89412689;0.0009634
+-107.9388733;38.94307327;0.004104246
+-103.3494034;46.97949982;0.002035548
+-84.5951004;37.11130142;0.011808495
+-78.66539764;41.41579819;0.000942799
+-84.90617371;36.35974884;0.002193466
+-95.44457245;45.58607483;0.209844212
+-96.47519684;32.06259918;0.019847965
+-83.06122589;39.34185028;0.111216426
+-107.8302536;43.83435059;0.000906784
+-98.45709991;34.63069916;0.00043835
+-78.11109924;36.37355042;0.001093422
+-99.30889893;38.52265167;0.006091533
+-82.41649628;31.01874924;0.005021944
+-101.7622223;38.91630173;0.057440453
+-80.26260376;35.76509857;0.00905551
+-96.66220093;33.67625046;0.028454435
+-108.2795029;38.41042328;0.004881483
+-88.84100342;35.61244965;0.007909074
+-113.0311508;39.0632019;0.000474266
+-98.046875;40.17625046;0.10239223
+-90.13297272;38.30414963;0.201202709
+-86.13232422;39.77975082;0.028391167
+-98.45155334;28.93124962;0.002789907
+-81.92945099;41.13259888;0.048555925
+-82.99821472;36.74319839;0.001787226
+-98.95970154;46.97814941;0.001477109
+-85.57505035;44.74634933;0.017134517
+-80.76117706;41.7384491;0.037147301
+-97.52611542;35.09519958;0.004246101
+-104.2866516;32.48265076;0
+-98.99970245;35.63850021;0.001267093
+-79.33989716;33.4530983;0.004026914
+-95.60847473;36.79835129;0.002765731
+-84.4733963;38.02752686;0.013180093
+-97.21472168;33.68249893;0.002861591
+-110.5647964;39.6406517;0.000211369
+-72.72325134;40.8852005;0.00205547
+-99.21327209;34.11349869;0.002091786
+-75.15969849;39.70154953;0.010586043
+-87.61634827;46.448349;0
+-92.21929932;38.53075027;0.018760857
+-82.62709808;30.21375084;0.001960354
+-96.23574829;35.02842331;0.001162099
+-110.4394531;40.31990051;0.000434289
+-82.51165009;39.21165085;0.004528111
+-121.7530975;47.43375015;0
+-77.56152344;37.39260101;0
+-79.23940277;41.4796257;0.001459871
+-103.0274963;44.09930038;0.000619113
+-93.84220123;37.73675156;0.005909482
+-103.5048523;36.36975098;0.009587141
+-96.8804245;35.7024765;0.001142011
+-82.947052;36.41815186;0.001926396
+-90.34394836;37.47764969;0.002072556
+-112.8153992;39.66270065;0.00041461
+-86.30439758;36.15359879;0.001369245
+-83.582901;45.03179932;0.011702081
+-96.34459686;30.65200043;0.013336918
+-104.9433975;48.27985001;0
+-92.43845367;38.22539902;0.003693479
+-94.35624695;39.03414917;0.046499901
+-90.2934494;35.13547516;0.02944876
+-95.36579895;31.25964928;0.003173517
+-110.6508026;39.10189819;0.000245684
+-109.5364532;40.16025162;0.000314101
+-98.06784821;29.1602993;0.016264142
+-97.61795044;46.10889816;0.014919942
+-98.78530121;39.78495026;0.003315446
+-86.5943985;30.68770027;0.000667999
+-81.44660187;29.9375;0.000460946
+-88.19029999;31.40670013;0.001735051
+-117.5645981;43.2207489;0.002007036
+-109.8025284;44.40952682;0.000180053
+-115.9697037;34.83615112;0
+-113.2589264;37.8117485;0
+-116.0283966;42.83850098;0.001282075
+-86.31984711;34.34954834;0.012123938
+-85.92455292;31.8335495;0.008615487
+-84.84604645;34.24409866;0.007482097
+-82.9450531;34.35174942;0.003364412
+-81.73090363;31.77599907;0.001948643
+-96.24165344;37.88835144;0.010830986
+-85.40574646;36.77830124;0.008174743
+-84.59349823;41.88470078;0.168815961
+-84.12615204;45.02845001;0.004279806
+-88.64880371;30.86650085;0.002940162
+-93.81099701;38.74694824;0.061337965
+-83.00975037;39.96900177;0.047175709
+-100.4778519;36.75094986;0.004736519
+-96.19035339;33.94105148;0.00257895
+-95.57324982;34.01327515;0.004441616
+-102.5147476;36.7499733;0.016178121
+-95.5700531;36.33634949;0.000925995
+-78.20549774;41.41749954;0.000393418
+-76.22989655;40.72280121;0.035129953
+-81.91149902;33.75870132;0.001245293
+-81.17987823;34.99264908;0.000686864
+-96.79100037;43.67457581;0.001929799
+-87.36064911;36.14680099;0.002232735
+-84.93477631;35.61709976;0.003461891
+-77.35624695;38.0135498;0.029341621
+-76.43609619;37.85689926;0.117817213
+-76.7227478;37.95835114;0.085690223
+-76.7227478;37.95835114;0.273118861
+-95.74810028;48.76934814;0.001315782
+-99.10890198;29.38960075;0.02553643
+-122.3963013;39.59180069;0.01307243
+-81.60555267;33.53829956;0.003495982
+-97.24105072;46.9347496;0.005222407
+-103.604248;39.04150009;0.002114679
+-104.8727036;47.32452393;0.001053457
+-97.40795135;35.94445038;0.001259319
+-93.43689728;35.54804993;0.003539499
+-90.51439667;38.25260162;0.007374772
+-86.91265106;45.85485077;0.002804415
+-85.44315338;33.30350113;0.001344549
+-95.30662537;37.55882645;0.050557499
+-99.83327484;34.76862335;0.001743148
+-97.79904938;31.3899498;0.009804994
+-79.16049957;35.46640015;0.004251535
+-81.65370178;35.7820015;0.002466846
+-75.13047791;39.37284851;0.015966687
+-90.48432159;45.20650101;0.027568061
+-85.76795197;30.84964943;0.002267074
+-80.23970032;36.40375137;0.00293937
+-97.94184875;35.94509888;0.000692137
+-103.4670029;48.8166008;0.001240542
+-82.8601532;30.7303009;0.002706431
+-99.88265228;43.37395096;0.001162056
+-87.70210266;30.75984955;0.003719422
+-92.87770081;37.27719879;0.002258452
+-84.52570343;32.34794998;0.003618869
+-82.33999634;32.13169861;0.008523115
+-83.41449738;32.67509842;0.002168392
+-101.0759277;39.78525162;0.009495195
+-86.18049622;36.78105164;0.011285826
+-96.98735046;36.09400177;0.001365941
+-84.22170258;31.5428009;0.011377427
+-91.33474731;43.29045105;0.15391441
+-97.73692322;44.84859848;0.005382756
+-100.8143463;34.9653511;0.001512471
+-85.83795166;33.76205063;0.002824754
+-114.1143036;47.59505081;0.000313803
+-88.59684753;35.41775131;0.021662276
+-95.13439941;35.2580986;0.001083133
+-99.00614929;44.54610062;0.001087653
+-97.40619659;35.15337372;0.002331611
+-80.54139709;35.89680099;0.020622785
+-84.60755157;30.54990005;0.003027338
+-80.47724915;35.6772995;0.012529092
+-103.8270035;46.93595123;0.004210371
+-88.21980286;45.02539825;0.053076495
+-100.1050034;45.76845169;0.002335925
+-99.91259766;40.17644882;0.065712515
+-93.29105377;38.29885101;0.017053266
+-86.12114716;30.63165092;0.000443237
+-90.8499527;36.66090012;0.002730472
+-76.99230194;37.5021019;0.03429969
+-95.89360046;36.7112999;0.001499448
+-88.99089813;34.48165131;0.012411838
+-73.87265015;43.0882988;0
+-99.83260345;43.85309982;0.004859112
+-87.46704865;35.80120087;0.006377343
+-82.56439972;32.40499878;0.003114721
+-80.17637634;34.59130096;0.007630541
+-76.60559845;39.46392441;0.050900867
+-99.82424927;37.23699951;0.001122163
+-93.82634735;36.71524811;0.001604498
+-85.67712402;38.18930054;0.004463242
+-96.73190308;39.04489899;0.00690491
+-100.6890488;31.39579964;0.001437159
+-96.13735199;40.92464828;0.00558854
+-97.03977966;45.61640167;0.001844445
+-98.35700226;35.20325089;0.002444596
+-80.30815125;40.98970032;0.060252871
+-98.27907562;44.41455078;0.011917057
+-99.88269806;39.34975052;0.012349854
+-101.6485977;40.85074997;0.206108891
+-89.48487854;34.74520111;0.012830405
+-96.64125061;38.69594955;0.016355861
+-82.79239655;32.99885178;0.004593292
+-119.5749283;45.45999908;0.002844571
+-87.45310211;33.3070488;0.003421438
+-90.73905182;44.33377457;0.048110035
+-121.5606995;39.72399902;0.000190608
+-78.92195129;36.80142593;0.002860674
+-96.88269806;35.18190002;0.003173797
+-98.35162354;45.58955002;0.003374789
+-115.6177979;43.38389969;0.000964641
+-84.82125092;36.80039978;0.01530529
+-101.5686722;44.99217606;0.000477748
+-98.75595093;38.47887421;0.037402125
+-101.4885788;36.74879837;0.059058935
+-84.36385345;38.70869827;0.005569616
+-79.43240356;35.28044891;0.001903467
+-92.7594986;35.26755142;0.008428482
+-103.4373016;48.29595184;0.000377332
+-86.10285187;44.34125137;0.004311455
+-89.18460083;34.78882599;0.012291354
+-86.03530121;34.02109909;0.003798007
+-83.82015228;45.41267395;0.009231975
+-89.94242859;31.90105057;0.00291942
+-86.38484955;35.26869965;0.008466868
+-79.44020081;40.35549927;0.022855151
+-98.99069977;35.28347397;0.000622913
+-99.90335083;39.78414917;0.010679623
+-82.53234863;32.15137482;0.008279664
+-80.58197784;34.3417511;0.00258172
+-92.50395203;39.42834854;0.043733932
+-78.62529755;35.79745102;0.001502548
+-103.0033493;41.7215004;0.057396839
+-86.44409943;31.26097488;0.002569354
+-85.0746994;42.94499969;0.176909422
+-97.09537506;38.34754944;0.003289364
+-102.6636505;46.82012558;0.001517943
+-101.1258011;42.54264832;0.00233306
+-76.54370117;37.42259979;0.044723235
+-85.77735138;35.68804932;0.010833391
+-98.13547516;37.55884933;0.012306712
+-98.46365356;48.77185059;0.001574614
+-98.68740082;34.01205063;0.002489405
+-97.64987183;39.48007584;0.021833909
+-88.76715088;33.65890121;0.010325901
+-93.70980072;35.22489929;0.005502768
+-95.95220184;35.61695099;0.002017662
+-94.40865326;39.28300095;0.03114356
+-100.3815994;40.52484894;0.057879278
+-76.42475128;41.042099;0.059854804
+-83.33664703;38.52249908;0.005160065
+-91.92694855;38.81482315;0.045449669
+-85.70627594;35.37334824;0.006933659
+-77.16484833;38.26309967;0.030381944
+-87.68765259;33.71995163;0.006721394
+-80.09564972;35.00450134;0.018518257
+-80.71595001;32.82770157;0.007691507
+-91.9178009;38.4966507;0.019592153
+-87.15209961;31.12789917;0.004617999
+-100.2729034;36.27772522;0.005196543
+-94.78164673;35.46794891;0.013913211
+-80.70709991;36.39920044;0.013396518
+-83.11634827;32.17625046;0.006246377
+-97.35160065;32.34434891;0.005997916
+-99.10700226;28.86580086;0.003723456
+-85.40209961;31.88222504;0.003531472
+-118.7025986;45.49862671;0.001020524
+-118.6631241;46.99879837;0.002678627
+-96.0134964;39.78337479;0.138189599
+-92.55425262;37.68429947;0.000816087
+-80.7766037;33.67274857;0.028355294
+-85.90834808;42.59382629;0.118963454
+-82.74185181;32.11389923;0.002886521
+-85.30410004;38.61449814;0.00944743
+-101.3542023;35.83977509;0.018488624
+-82.92590332;31.96685028;0.003188125
+-82.28105164;40.81007385;0.099043877
+-85.44535065;37.76620102;0.036231455
+-81.12039948;32.79199982;0.024842349
+-85.06069946;45.24084854;0.005997505
+-81.99755096;32.05379868;0.013244537
+-82.91555023;42.67229843;0.031546603
+-80.76145172;41.31655121;0.043087367
+-98.22389984;26.4225502;0.013835802
+-89.5530014;32.42810059;0.006413361
+-89.93624878;34.66297531;0.027040892
+-93.51107788;39.78939819;0.076879724
+-97.47727203;37.23789978;0.001189912
+-97.64992523;38.78352356;0.002171315
+-100.9061508;38.4823494;0.040504097
+-100.3707504;37.23770142;0.114023119
+-87.61560059;32.74354935;0.003398111
+-79.12729645;40.63985062;0.023929179
+-99.45310211;41.9151001;0.013438855
+-102.3310471;41.61539841;0.012284382
+-95.73310089;38.23625183;0.056818182
+-83.08387756;31.01772499;0.012545498
+-84.63339996;35.44279861;0.007631133
+-98.68589783;43.16957474;0.008968676
+-104.3534775;42.07842636;0.016500436
+-96.09729767;41.09550095;0.025985365
+-97.65035248;39.13227463;0.002600158
+-98.78079987;42.87995148;0.008679752
+-89.28170013;37.46519852;0.059322136
+-88.53269958;34.61009979;0.010543947
+-97.48004913;37.69345093;0.026266285
+-101.3024979;34.07144928;0.007086596
+-89.48400116;34.35812378;0.002970956
+-94.33860016;36.90230179;0.006235832
+-89.64134979;33.08262634;0.002763117
+-99.78032684;46.97904968;0.00532096
+-97.39454651;47.9334259;0.003477511
+-101.3460236;38.48225021;0.044144561
+-122.2919998;39.16930008;0.004345257
+-74.94934845;40.56420135;0.023622482
+-82.70465088;42.84334946;0.126290465
+-95.15057373;40.73752594;0.300919001
+-91.90309906;44.94660187;0.123418483
+-85.35494995;31.15579987;0.011038407
+-80.79740143;33.44169998;0.04943905
+-91.41615295;40.59627533;0.229517385
+-89.98999786;32.31715012;0.007060825
+-96.92147827;46.28279877;0.001522576
+-93.83380127;37.10634995;0.01095901
+-87.09742737;36.24869919;0.004646303
+-83.25307465;30.82805061;0.008676768
+-84.06330109;41.89450073;0.19091439
+-81.25767517;33.92499924;0.011843046
+-78.88234711;35.38755035;0.009716223
+-86.39442444;36.98217392;0.073652813
+-102.608551;38.82979965;0.014472872
+-82.11184692;32.4144516;0.018986957
+-102.3992233;42.50215149;0.012417863
+-93.60669708;33.24985123;0.028193257
+-98.53522491;46.45645142;0.002860275
+-96.08674622;40.65352631;0.006344508
+-95.80854797;47.32519913;0.004760924
+-100.8117523;35.40169907;0.007742815
+-83.60250092;44.33667374;0.011382055
+-95.74005127;37.88650131;0.044320928
+-95.21514893;36.75469971;0.006775357
+-96.68840027;40.78454971;0.013410738
+-84.13954926;41.01345062;0.235729638
+-96.31977844;29.84320068;0.009816654
+-86.14755249;33.39120102;0.013310819
+-82.6957016;32.66735077;0.006676034
+-85.13379669;38.67354965;0.010809824
+-76.34484863;37.45287323;0.012765523
+-91.52574921;35.73452377;0.015343096
+-121.998703;38.28677368;0.011306214
+-99.48770142;44.54515076;0.012159251
+-88.98104858;39.85390091;0.194404641
+-101.6691513;43.19255066;0.003295607
+-92.77905273;38.87234879;0.107018654
+-102.4262009;40.00389862;0.116570469
+-95.72509766;38.65192413;0.055525942
+-83.22219849;43.10374832;0.075712703
+-91.05049896;38.45154953;0.027768446
+-98.08529663;37.9531517;0.022794399
+-96.79049683;44.36965179;0.016520656
+-104.5615768;38.82474899;0
+-77.28649902;36.90930176;0.015283353
+-76.6888504;39.97299957;0.128358395
+-101.694725;41.56914902;0.002184184
+-118.8313522;46.46620178;0.012576465
+-75.31105042;39.57931519;0.070291228
+-102.9953537;41.22069931;0.018937969
+-103.7093964;41.54629898;0.008375546
+-96.68965149;40.26229858;0.059921196
+-85.30715179;38.04417419;0.025215169
+-78.29350281;34.10504913;0.008956878
+-98.73940277;37.64775085;0.10863243
+-86.34745026;37.47045135;0.018613008
+-83.23795319;31.74975014;0.035371738
+-98.52839661;41.91464996;0.076879444
+-103.7289276;37.95429993;0.005567719
+-81.96264648;39.96110153;0.028446278
+-76.89955139;37.09609985;0.033591315
+-88.54167175;42.66754913;0.26027129
+-102.3520508;40.8757515;0.074100759
+-92.53434753;40.47029877;0.045169227
+-99.93122864;42.46519852;0.044139141
+-85.19864655;43.29370117;0.112987967
+-86.25910187;37.19440079;0.007744862
+-85.84459686;35.98160172;0.00935909
+-96.23670197;40.13159943;0.009775355
+-90.86985016;37.97109985;0.063396977
+-97.89277649;42.96749878;0.008043544
+-88.90325165;34.79579926;0.012966522
+-83.83679962;42.25325012;0.085174451
+-79.73925018;41.3980484;0.010647517
+-98.49992371;40.17639923;0.088603907
+-94.37527466;32.78580093;0.01444085
+-98.20894623;39.3932991;0.003572347
+-102.6018524;35.83879852;0.10952727
+-76.91674805;37.7011261;0.056816385
+-89.7193985;34.02954865;0.004348454
+-101.0608521;40.52489853;0.074497623
+-86.6989212;41.04097366;0.401361603
+-81.87210083;32.16320038;0.00929456
+-87.83119965;36.50497437;0.005112001
+-90.97699738;32.00550079;0.01283974
+-113.6716003;42.86455154;0.00041139
+-84.38684845;43.98770142;0.0175735
+-77.41600037;37.54040146;0.011157901
+-94.69962311;40.73559952;0.203378125
+-96.22364807;38.9681015;0.004115039
+-95.6590271;40.13090134;0.001412085
+-76.02140045;38.47634888;0.017285792
+-101.6016998;48.32740021;0.003648379
+-86.11470032;38.21009827;0.08050472
+-86.57700348;35.17795181;0.024661102
+-96.83882141;45.15174866;0.003434016
+-97.42700195;38.04339981;0.044905253
+-98.34697723;44.93759918;0.012883427
+-92.6006012;40.19179916;0.032242063
+-87.14409637;32.3879509;0.00286782
+-86.47989655;36.44874954;0.01476006
+-82.32015228;33.83182526;0.017571975
+-93.03992462;36.65729904;0.022323143
+-89.32375336;33.94200134;0.014651601
+-80.55899811;35.01110077;0.061287008
+-77.64745331;37.07089996;0.007755574
+-93.28115082;38.72449875;0.114309235
+-77.39935303;34.71440125;0.021191414
+-76.19519806;35.38359833;0.068843832
+-97.18804932;44.67385101;0.01140601
+-79.33644867;33.99895096;0.010097246
+-84.16815186;40.520401;0.226635514
+-100.4829254;38.91537476;0.008312877
+-80.2717514;36.11645126;0.005340576
+-94.81259918;36.83345032;0.012266082
+-77.53224945;36.71955109;0.005288722
+-78.27680206;36.04175186;0.0030169
+-98.50579834;46.1093483;0.010914014
+-92.61820221;38.67504883;0.043515317
+-77.73718262;39.52159882;0.186357472
+-87.0305481;35.22344971;0.017391518
+-94.50924683;45.10932541;0.277816577
+-101.7305222;39.78564835;0.03493058
+-85.99275208;31.40055084;0.011275939
+-88.18164825;35.20930099;0.018927595
+-85.62110138;34.23194885;0.006779722
+-89.0652771;43.80754852;0.192732443
+-101.7989731;37.19110107;0.032537812
+-81.73184967;32.40074921;0.022908206
+-99.45559692;42.44530106;0.032537235
+-103.713974;41.19805145;0.013954997
+-83.69264984;43.00194931;0.068886405
+-96.53514862;31.02599907;0.012798984
+-85.96672821;34.73329926;0.028968982
+-83.48664856;38.82649994;0.023280557
+-88.98960114;35.21305084;0.014043892
+-102.4621277;46.41815186;0.002069993
+-102.3944016;37.95610046;0.007620186
+-100.8501968;37.19287491;0.144395406
+-95.58390045;33.35615158;0.016070164
+-86.50164795;39.16934967;0.015056422
+-100.5215759;46.98065186;0.008610977
+-78.80660248;35.0489006;0.021065694
+-91.04472351;32.71120071;0.128613277
+-76.63180542;37.64199829;0.063555257
+-89.26294708;33.59965134;0.004807806
+-80.98077393;34.00569916;0.008469283
+-99.2507019;44.06475067;0.00531248
+-101.7309494;47.2778511;0.000896709
+-95.15579987;41.03034973;0.331756629
+-95.30135345;37.88542557;0.052807101
+-85.57737732;44.33874893;0.003868327
+-97.18800354;44.97777557;0.003181032
+-96.17285156;41.29175186;0.028324974
+-91.45345306;38.92145157;0.129660812
+-97.6568222;46.45657349;0.022638366
+-87.39689636;40.97755051;0.393881423
+-85.23179626;31.5352993;0.008343717
+-76.10088348;36.29735184;0.107767575
+-103.0710526;42.22055054;0.060884738
+-88.23815155;37.5006485;0.026285538
+-93.57119751;40.42205048;0.055044256
+-88.07272339;36.08684921;0.011871897
+-99.24765015;48.68524933;0.006557757
+-83.65070343;40.99297333;0.240837019
+-103.0719757;37.95555115;0.001032103
+-83.83384705;31.58325005;0.009598845
+-88.56140137;37.33435059;0.020644583
+-76.1223526;35.84647369;0.10298851
+-93.1059494;39.86864853;0.045841057
+-89.97104645;35.19955063;0.016566604
+-99.72595215;42.86157608;0.008486467
+-88.59335327;34.8756485;0.030086399
+-83.39489746;31.98857498;0.009449776
+-103.7067032;41.85100174;0.162110934
+-97.16490173;39.34944916;0.0203855
+-99.88445282;48.1962738;0.012127636
+-96.36602783;39.34629822;0.009253885
+-96.04109955;48.05445099;0.004054887
+-85.32375336;43.64085007;0.038521928
+-89.58399963;33.48187637;0.009217156
+-78.94110107;33.93564224;0.020949474
+-81.4769516;33.29272461;0.013390985
+-88.87670135;37.45175171;0.026749703
+-85.97789764;37.72254944;0.053494936
+-96.20694733;43.08449936;0.438504389
+-82.88500214;31.30154991;0.013404479
+-75.60054779;39.5679512;0.067812067
+-89.6019516;42.67874908;0.209763866
+-95.62640381;40.74060059;0.354641152
+-83.43489838;31.18860054;0.010916077
+-78.38565063;35.53575134;0.015391173
+-82.45535278;33.03715134;0.024282195
+-85.67935181;37.57695007;0.086673632
+-90.04879761;43.94499969;0.058419966
+-86.63839722;38.0542984;0.03277485
+-76.91425323;37.94520187;0.100016488
+-78.57714844;34.61512375;0.043217249
+-92.33719635;38.94599915;0.049011205
+-87.19300079;36.85684967;0.181845357
+-94.33840179;38.64720154;0.061919698
+-100.3529282;47.58739853;0.002894263
+-91.18849945;38.77019882;0.069885785
+-101.6524963;41.19919968;0.084804855
+-92.92445374;45.51234818;0.08380624
+-82.8707962;31.58989906;0.013827434
+-97.19300079;36.37662506;0.002988796
+-76.68464661;36.89899826;0.043530566
+-86.13110352;36.39260101;0.006838673
+-83.78555298;31.91690063;0.006276934
+-84.1114502;41.3266983;0.277611044
+-89.07250214;42.6697998;0.324657862
+-91.25215149;42.86285019;0.255798911
+-96.83785248;37.78167343;0.032500788
+-88.39624786;35.62049866;0.027042229
+-96.19080353;41.53839874;0.037612999
+-77.2059021;39.1440239;0.224228787
+-83.76170349;31.18020058;0.00707308
+-100.6643753;38.0006485;0.077778717
+-81.92915344;33.04909897;0.011288788
+-121.6795273;39.02019882;0.01400322
+-83.9278717;30.86795044;0.021657852
+-94.15509796;33.7424736;0.014692607
+-96.54814911;42.14649963;0.027773546
+-85.49160004;39.29195023;0.029180728
+-90.0897522;32.64229965;0.050985902
+-99.91607666;38.47992325;0.001163061
+-82.32465363;32.57294846;0.005695986
+-79.69869995;34.04192352;0.029107384
+-89.35890198;38.08464813;0.174335541
+-81.61579895;32.76720047;0.017349331
+-77.07209778;37.35720062;0.043602265
+-98.95227814;40.17634964;0.142977757
+-79.24710083;35.020401;0.007588635
+-88.46025085;33.51615143;0.036706152
+-84.97975159;31.63299942;0.010405461
+-79.71320343;33.59474945;0.022754042
+-86.06334686;42.24489975;0.082107602
+-95.29170227;38.21390152;0.078001698
+-84.75274658;31.77510071;0.01456028
+-95.74102783;37.19284821;0.052067188
+-94.34279633;36.63249969;0.040823245
+-89.8219986;33.78704834;0.008520235
+-89.41404724;35.19924927;0.028832505
+-90.04174805;37.31959915;0.025675001
+-102.7751999;38.44020081;0.000441137
+-86.52367401;42.00149918;0.114109019
+-93.98545074;39.33520126;0.096855409
+-94.69965363;41.02780151;0.234260637
+-95.45375061;45.93349838;0.111339702
+-89.71859741;40.74399948;0.284746659
+-101.7853775;37.99969864;0.013641632
+-82.82299805;40.52854919;0.151934297
+-97.39849854;42.98485184;0.011084747
+-100.4820786;41.56667328;0.030117579
+-76.62840271;35.15995026;0.045445777
+-88.95510101;33.90674973;0.009034313
+-88.9694519;36.86270142;0.178580705
+-84.39109802;31.2635498;0.037330284
+-91.3085022;35.62210083;0.030829729
+-91.57362366;34.78549957;0.007256978
+-91.54434967;40.98740005;0.302113065
+-85.21250153;38.19760132;0.039043182
+-92.98345184;39.46640015;0.135398614
+-75.53614807;39.09764862;0.12082005
+-86.67030334;37.19749832;0.046720239
+-94.24310303;40.734375;0.12205442
+-85.52940369;42.24509811;0.129869985
+-91.91394806;38.15174866;0.179765791
+-92.686203;39.15724945;0.106349573
+-76.24334717;36.28839874;0.151511812
+-84.87979889;30.8933506;0.062358217
+-86.2335968;37.99549866;0.049127257
+-101.3541489;36.27747345;0.056568004
+-75.75017548;38.10010147;0.063032915
+-84.42479706;42.24767685;0.103930795
+-83.81739807;32.15980148;0.00477245
+-83.5026474;31.46190071;0.007663648
+-86.41474915;37.80244827;0.031391398
+-96.26462555;40.3926239;0.023263233
+-96.61227417;41.23419952;0.111710771
+-119.4890976;47.29534912;0.013054538
+-82.63400269;31.82010078;0.009842372
+-87.90840149;36.81755066;0.075811857
+-87.13647461;37.22822571;0.041800956
+-76.94934845;36.3906517;0.046000113
+-86.47917175;38.83955002;0.044560301
+-99.2852478;37.55825043;0.049531763
+-86.83615112;36.85820007;0.154652858
+-86.26055145;41.32532501;0.302110766
+-87.37905121;36.48149872;0.043460725
+-82.46510315;40.40570068;0.125090112
+-96.22962189;43.37889862;0.384308511
+-79.51319885;34.83660126;0.005385567
+-93.02749634;40.46649933;0.026851226
+-90.13212585;42.66025162;0.246371751
+-84.15654755;39.02097321;0.041137525
+-93.98860168;39.96127319;0.087361324
+-86.80055237;37.82714844;0.048828125
+-91.72650146;33.5910759;0.005660001
+-97.78665161;36.79607391;0.002030113
+-86.70950317;41.4986496;0.184552195
+-91.07845306;36.3118515;0.011985671
+-98.8980484;47.71747589;0.009670777
+-76.68782806;36.42409897;0.027982884
+-85.16809845;39.6569519;0.249319641
+-100.7462006;41.04642487;0.093726355
+-86.55049896;34.7371254;0.024264834
+-90.66300201;35.57339859;0.008248212
+-92.01065063;39.50030136;0.112716338
+-97.50585175;26.14155006;0.008452846
+-77.04425049;39.53525162;0.146420316
+-84.57164764;40.83732605;0.263661635
+-76.96724701;36.03039932;0.039107753
+-88.44229889;35.96984863;0.071731745
+-82.81195068;43.42214966;0.145016984
+-77.39700317;39.46989822;0.068660993
+-97.16212463;43.29167557;0.024571593
+-84.18695068;33.78609848;0.019897788
+-95.76304626;44.02225113;0.379074216
+-90.96324921;39.04914856;0.091694555
+-86.82830048;39.66830063;0.213403843
+-93.32685089;40.739151;0.130802892
+-101.3540497;35.40380096;0.019463762
+-84.18334961;32.05120087;0.022538534
+-93.0305481;44.69544983;0.236193774
+-84.38745117;43.64725113;0.055162225
+-96.62460327;42.78319931;0.066521135
+-89.89937592;38.01305008;0.143710453
+-92.06964874;39.77935028;0.126327531
+-92.86930084;41.02944946;0.090128285
+-97.8973465;42.65987396;0.037789229
+-93.55827332;41.3362999;0.179035319
+-87.7723999;39.32220078;0.3277667
+-95.67845154;46.9341507;0.016931594
+-93.28230286;44.37009811;0.246510541
+-92.46977234;37.27054977;0.065074854
+-91.94785309;40.7492981;0.13858236
+-96.98274994;42.89419937;0.02163812
+-95.80970001;40.41192627;0.006107989
+-100.8154984;36.27799988;0.018220879
+-96.09559631;47.86222458;0.012285225
+-84.8715744;31.29475021;0.019866303
+-95.00614929;45.1521759;0.273416891
+-94.40384674;39.60129929;0.119773259
+-86.1013031;39.49007416;0.242531778
+-99.72740173;41.39392471;0.087111903
+-97.19355011;41.91699982;0.058162529
+-93.57185364;41.67762375;0.248364363
+-84.302948;37.71220016;0.053810391
+-82.60904694;39.74795151;0.165805468
+-88.55905151;33.10710144;0.063868219
+-95.57927704;41.33300018;0.362682864
+-88.05854797;37.02334976;0.036219286
+-77.16492462;36.76974869;0.027363817
+-93.99770355;40.35485077;0.088992663
+-76.66525269;40.89434814;0.115851471
+-86.49539948;38.54119873;0.075481202
+-83.62807465;31.71080017;0.009832885
+-86.80314636;38.70230103;0.05578033
+-88.68364716;34.29180145;0.020157254
+-93.32767487;41.02965164;0.07983974
+-89.4240036;38.36045074;0.243841204
+-90.83934784;32.35977554;0.025570652
+-83.23594666;31.25065041;0.015887138
+-90.16145325;40.44940186;0.257763222
+-89.3947525;43.46215057;0.214447718
+-86.87615204;38.36460114;0.196543165
+-85.42355347;41.39509964;0.218919511
+-101.8056488;38.48099899;0.012853305
+-94.47112274;41.33050156;0.288182856
+-86.8477478;36.51100159;0.098379751
+-99.47619629;46.45615005;0.008657469
+-83.86299896;38.94689941;0.06259405
+-99.87190247;38.91410065;0.006156492
+-84.25305176;31.26029968;0.032045961
+-78.61662292;34.21369934;0.052872812
+-94.33184814;38.25859833;0.072741228
+-94.7385025;44.30265045;0.368077383
+-93.84980011;37.43175125;0.021349982
+-84.13884735;40.78170013;0.22562619
+-101.7189484;39.35079956;0.084207473
+-91.87969971;34.77429962;0.010199619
+-96.32409668;41.86505127;0.095125515
+-97.07945251;41.56060028;0.154306046
+-88.46219635;33.86980057;0.025763246
+-91.2542038;35.17910004;0.031966039
+-91.22395325;34.67029953;0.073405176
+-91.69415283;40.10115051;0.157484505
+-85.00060272;41.6432991;0.167526399
+-88.57465363;35.18999863;0.036467684
+-94.85134888;39.34452438;0.140886327
+-77.49269867;36.35725021;0.005533757
+-88.26934814;36.62385178;0.147653722
+-89.82332611;37.72537613;0.075381318
+-84.17115021;39.4225502;0.069595626
+-84.13845062;41.60285187;0.316894267
+-94.01525116;41.33430099;0.174031882
+-85.71035004;38.43724823;0.059160356
+-95.15055084;43.37810135;0.354684958
+-88.73789978;36.28274918;0.166971165
+-96.61722565;41.56800079;0.178649956
+-86.51057434;39.76234818;0.22382098
+-101.6966019;40.52474976;0.215902739
+-97.1059494;36.79632568;0.013095951
+-90.3927536;34.64220047;0.019925903
+-93.7866745;40.73609924;0.077860568
+-87.88790131;37.16952515;0.090062828
+-84.72975159;31.16365051;0.060722487
+-94.92735291;41.33119965;0.332252406
+-88.37954712;37.20294952;0.029660213
+-88.26014709;36.31095123;0.089018658
+-98.9809494;41.56740189;0.157319885
+-88.07975006;37.34669876;0.032359723
+-92.08950043;43.67442322;0.266146589
+-89.60575104;39.74985123;0.24081887
+-91.17882538;40.88550186;0.274089871
+-84.2684021;39.75045013;0.101531364
+-89.42250061;38.58074951;0.259631402
+-95.23014832;44.44680023;0.403710513
+-84.44519806;31.79364967;0.038204073
+-89.41564941;37.76264954;0.069607393
+-84.57254791;41.12134933;0.19068611
+-94.82315063;39.97505188;0.176291994
+-95.56404877;39.82654953;0.277360383
+-87.70700073;34.87467575;0.016804589
+-94.85247803;37.50650024;0.077214501
+-94.33499908;37.20600128;0.030286214
+-76.84925079;35.47840118;0.080958549
+-76.59700012;35.85354996;0.114342392
+-83.45355225;32.25360107;0.004420621
+-84.84735107;43.6405983;0.081353283
+-97.60362244;45.36989975;0.085829112
+-99.82042694;40.85932541;0.279976454
+-83.4016037;43.47494888;0.151743843
+-97.14234924;40.17589951;0.102256069
+-92.14775085;40.12680054;0.101033695
+-92.14714813;40.45330048;0.120447237
+-89.96179962;34.3567009;0.017584405
+-87.24272156;38.06195068;0.155001041
+-91.68489838;40.43114853;0.149378585
+-88.65164948;36.72394943;0.163114887
+-86.46882629;39.48600006;0.167217138
+-101.3112717;37.19184875;0.196512515
+-95.62374878;42.73542404;0.362773976
+-92.4083786;40.74580002;0.100908612
+-95.15422821;43.67419815;0.402822115
+-86.84279633;39.3193512;0.055169012
+-94.41020203;40.21120071;0.088691711
+-84.61795044;40.53995132;0.239466186
+-79.35241699;34.42515182;0.028560666
+-89.90975189;33.44549942;0.010454335
+-86.07804871;38.59534836;0.103575386
+-89.96690369;35.71875;0.020004175
+-79.97360229;34.30884933;0.032021866
+-82.48280334;40.09505081;0.094683176
+-77.62840271;36.25709915;0.009047232
+-94.4041214;39.89279938;0.088033357
+-89.82522583;35.50654984;0.054422361
+-88.94104767;36.00695038;0.14777988
+-96.78717804;41.91630173;0.06364729
+-91.84294891;43.29127502;0.274158582
+-94.90065002;40.35165024;0.192144259
+-94.85179901;39.67634964;0.165886681
+-94.06945038;44.05599976;0.379431057
+-95.8430481;44.4129982;0.373029181
+-87.26767731;38.38835144;0.143617259
+-101.7342987;34.53030014;0.010932277
+-86.89495087;40.38869858;0.312943936
+-88.41995239;41.93690109;0.253991815
+-92.38424683;44.01435089;0.242387176
+-84.92514801;39.62599945;0.305632931
+-90.18789673;33.92950058;0.072068347
+-91.4477005;39.50239944;0.141321656
+-92.64199829;44.45449829;0.25819464
+-92.74890137;43.67427444;0.399683767
+-75.5904007;37.74407578;0.082374132
+-96.38999939;47.77999878;0.010784776
+-85.79122162;39.5231514;0.21437395
+-90.96714783;39.62034988;0.295638625
+-83.88069916;39.70064926;0.205631749
+-85.05709839;39.3974762;0.12386658
+-87.07610321;41.46474838;0.227212934
+-93.79570007;39.10070038;0.226686768
+-96.62609863;28.3953495;0.032939041
+-91.86139679;39.20360184;0.165244914
+-91.83820343;34.27754974;0.016776011
+-91.71534729;41.33667374;0.320077456
+-94.24214935;41.0271492;0.187769504
+-87.78314972;34.72940063;0.017608447
+-86.85904694;37.47510147;0.052628033
+-77.05014801;35.11729813;0.024040837
+-82.68599701;35.86902618;0.011519613
+-84.93800354;40.74554825;0.276255304
+-75.84447479;37.3199501;0.043702078
+-90.80394745;42.48474884;0.273622484
+-88.93005371;37.72975159;0.038009849
+-92.86834717;40.74140167;0.080289673
+-85.42684937;41.64157486;0.281816244
+-97.53710175;48.77145004;0.013407821
+-89.28559875;35.60985184;0.035165041
+-89.98355103;38.43984985;0.185473246
+-79.14320374;34.62639236;0.069822099
+-96.24375153;29.29829979;0.067939145
+-90.69380188;39.1344986;0.11388484
+-95.81839752;41.68597412;0.354338902
+-96.5414505;42.40052414;0.058638173
+-92.53314972;42.07989883;0.344430195
+-90.52674866;42.20740128;0.208795139
+-83.86605072;44.03694916;0.046852851
+-96.82430267;42.51259995;0.035750645
+-98.77799988;42.48875046;0.080629368
+-95.29772186;37.19172668;0.035159983
+-87.49649811;36.89704895;0.158130233
+-97.14035034;40.52405167;0.149911357
+-98.20337677;47.93365097;0.015121687
+-97.71777344;48.36875153;0.011702841
+-86.58194733;36.7582016;0.219632499
+-96.01345062;42.03889847;0.367450802
+-78.38549805;34.9341011;0.041316304
+-97.0951004;42.22117615;0.058140518
+-89.12580109;36.57474899;0.19292893
+-82.91850281;40.84989929;0.2922086
+-101.8264999;34.06869888;0.025972976
+-88.23957825;39.27510071;0.309772556
+-88.86374664;40.51959991;0.208830446
+-89.32109833;37.15855026;0.044948811
+-85.00430298;42.24622345;0.144847401
+-91.20445251;40.41614914;0.308323475
+-78.05549622;35.38574982;0.073520097
+-94.34545135;37.50147629;0.067574674
+-83.60852814;39.19865036;0.085004428
+-99.18624878;48.10874939;0.022408735
+-93.76824951;44.36980057;0.305183946
+-88.92430115;38.30075073;0.11220138
+-82.99755096;40.28490067;0.114783233
+-90.66134644;35.8478508;0.010990518
+-84.05554962;43.7378006;0.162492966
+-77.58429718;35.91450119;0.034032632
+-84.57379913;41.5658493;0.177834685
+-86.05225372;40.07249832;0.206925039
+-85.0707016;41.09360123;0.175684671
+-84.61955261;40.1352005;0.276915222
+-96.25232697;43.67487335;0.388836224
+-87.45075226;39.87779999;0.203759391
+-89.1704483;42.32524872;0.253958715
+-99.40450287;40.17655182;0.121544738
+-84.60690308;43.29185104;0.200338081
+-83.47975159;41.90895081;0.170114317
+-88.94145203;37.99407578;0.140673534
+-86.01629639;40.78094864;0.278705873
+-93.17254639;39.16965103;0.25788333
+-91.59962463;41.64260101;0.255551442
+-83.64904785;41.39242554;0.230330299
+-84.03279877;43.34859848;0.169977625
+-84.22989655;40.03984833;0.24222128
+-77.92545319;35.71974945;0.023579186
+-83.24990082;31.62075043;0.044666629
+-96.24424744;42.73550034;0.37182564
+-95.75257874;43.6745491;0.399694572
+-90.52365112;36.11629868;0.018939066
+-86.11889648;40.46955109;0.366273416
+-92.64009857;41.33544922;0.32434048
+-90.28859711;39.9992485;0.353734371
+-87.55449677;37.33784866;0.062436428
+-85.72924805;38.68904877;0.36313433
+-90.05535126;33.1167984;0.031001984
+-93.09784698;41.33459854;0.208630303
+-83.31369781;40.84009933;0.238452578
+-98.23474884;31.2466507;0.033211639
+-102.7835464;34.52997589;0.048913413
+-84.51560211;41.29655075;0.175190315
+-77.68460083;43.15254974;0.036971591
+-85.79244995;40.84885025;0.261696154
+-94.49295044;41.68315125;0.275143921
+-91.21109772;39.97859955;0.231831421
+-96.66805267;44.76050186;0.131304627
+-89.64520264;37.36349869;0.065619491
+-87.73654938;39.01404953;0.285352738
+-97.13670349;41.25040054;0.179920835
+-93.78569794;41.02939987;0.097855585
+-87.10320282;41.01134872;0.389599371
+-87.11274719;37.74544907;0.217619326
+-84.21859741;40.33359909;0.202341975
+-85.01584625;39.86045074;0.219137944
+-98.50174713;40.87250137;0.491855783
+-95.74357605;37.55924988;0.036760862
+-97.99725342;41.39580154;0.132061203
+-87.57517242;37.9986496;0.220483225
+-85.44280243;38.74995041;0.059668761
+-90.71495056;39.97309875;0.237710617
+-90.4108963;36.71234894;0.028448189
+-83.76554871;40.14175034;0.276361536
+-89.00205231;36.6487999;0.249294305
+-83.36090088;40.30697632;0.152081186
+-88.53845215;38.08145142;0.210411113
+-80.24034882;34.17739868;0.057122837
+-93.34889984;43.6739502;0.388174072
+-83.12560272;41.1242981;0.205461414
+-76.78939819;38.12115097;0.078404529
+-95.2989502;41.68444824;0.404621465
+-90.3180542;41.55495071;0.242750439
+-90.55609894;40.13135147;0.334394429
+-95.15067291;43.08264923;0.399626905
+-77.65180206;35.50970078;0.064760739
+-89.1005249;37.20119858;0.129955426
+-82.11289978;41.28879929;0.042195431
+-94.03520203;41.68315125;0.331698552
+-85.05905151;41.91595078;0.238034824
+-95.16030121;44.02194977;0.414313114
+-86.89354706;40.03984833;0.365763315
+-84.62915039;31.53560066;0.02398012
+-90.29250336;34.29167557;0.025087071
+-88.9986496;37.07355118;0.147440874
+-92.86180115;44.02265167;0.36511661
+-88.91950226;38.65000153;0.132686676
+-98.51734924;41.22014999;0.20550907
+-76.54055023;36.16175079;0.030772127
+-92.40930176;41.03044891;0.199016929
+-89.44390106;36.0469017;0.082639569
+-89.1359024;35.84472656;0.032396238
+-88.9756012;38.97719955;0.204774316
+-95.27400208;40.06892395;0.316309243
+-94.89679718;41.6834259;0.356131384
+-93.56285095;40.11307526;0.079949865
+-82.64790344;41.38705063;0.149580195
+-100.0930786;44.32595062;0.007381026
+-97.13909912;40.87239838;0.188397129
+-88.20159912;37.74515152;0.349925092
+-82.99915314;39.64025116;0.25527485
+-84.97605133;39.12760162;0.170510913
+-94.67890167;42.73424911;0.458445127
+-93.94795227;43.67385101;0.434617729
+-77.38970184;35.01205063;0.020529584
+-88.15354919;39.01094818;0.293916869
+-77.43544769;35.58670044;0.035250852
+-88.21600342;39.53004837;0.355665565
+-98.74584961;48.22949982;0.046155632
+-87.02204895;34.77484894;0.015403574
+-85.86437225;41.23929977;0.261918605
+-95.53177643;42.38619995;0.396316393
+-83.12999725;41.3769989;0.210787429
+-91.21697235;41.24805069;0.297400602
+-88.42784882;41.59080124;0.385531693
+-90.60790253;41.61054993;0.361681916
+-95.68195343;45.2820015;0.320683093
+-77.61160278;35.21720123;0.064868076
+-91.94670105;41.03165054;0.226116369
+-92.06430054;41.68629837;0.28508398
+-94.61380005;43.97857666;0.431160963
+-89.78652191;40.01884842;0.397772474
+-82.58924866;41.14139938;0.176010778
+-92.53250122;41.68610001;0.317825582
+-90.28277588;42.35172653;0.190282492
+-90.75524902;34.76824951;0.029865289
+-89.7492981;36.20262527;0.069081082
+-98.06244659;41.69844818;0.225720485
+-77.92355347;34.9524498;0.069743375
+-89.44454956;38.88444901;0.218634929
+-86.05175018;40.31129837;0.426643688
+-85.39880371;39.93199921;0.280743389
+-86.69824982;41.30205154;0.295011477
+-93.05169678;41.68555069;0.336049124
+-99.07364655;40.85022354;0.242286852
+-96.09449768;45.03689957;0.310918849
+-87.31809998;34.55182648;0.023210577
+-92.99877167;42.03637314;0.375390953
+-87.82109833;38.41009903;0.345404305
+-97.60077667;42.26435089;0.162258464
+-87.40155029;39.43360138;0.175122121
+-83.78309631;39.90380096;0.220780398
+-76.35307312;36.19950104;0.082812437
+-94.35514832;44.31824875;0.379665148
+-98.97564697;41.22060013;0.126471264
+-85.49700165;41.14844894;0.219808059
+-85.71974945;40.16159821;0.274395638
+-92.17839813;41.33634949;0.27516877
+-99.14450073;45.07125092;0.063584848
+-87.70619965;38.71025085;0.281853191
+-87.65405273;37.49914932;0.15403286
+-90.21324921;40.93235016;0.336155205
+-98.5210495;41.56757355;0.132436167
+-91.59889984;42.07939911;0.288569427
+-83.13947296;40.56930161;0.199645899
+-99.66717529;47.58717346;0.020771388
+-90.45894623;39.65515137;0.347309198
+-88.42504883;38.43119812;0.173781342
+-95.1223526;42.38555145;0.40974176
+-85.25869751;40.74219894;0.31295613
+-94.84619904;37.16897583;0.06333884
+-90.98504639;40.85345078;0.365866133
+-89.15480042;36.35449982;0.189251436
+-94.6556778;42.38465118;0.445332369
+-86.20690155;41.04085159;0.362948088
+-93.22582245;44.02244949;0.363025259
+-93.45809937;36.74710083;0.174929273
+-83.76734924;40.38394928;0.188478863
+-95.90564728;44.73889923;0.35601318
+-91.07630157;41.46595001;0.30632366
+-101.0587463;41.56845093;0.135354629
+-91.84382629;42.86259842;0.350585563
+-87.21504974;39.77890015;0.188650876
+-90.81085205;32.87982559;0.111421875
+-84.64689636;39.74300003;0.228784134
+-86.96154785;39.03699875;0.09633489
+-97.54142761;41.53817368;0.264307151
+-92.06472778;42.08039856;0.372963749
+-88.47399902;38.7574501;0.226425147
+-85.76415253;39.82154846;0.350148635
+-93.5869751;44.02214813;0.424173066
+-95.47419739;40.42335129;0.346508298
+-98.22985077;47.45642471;0.013452717
+-85.99255371;41.91559982;0.186352046
+-83.43045044;39.90345001;0.258156261
+-85.88504791;39.19297409;0.21891284
+-87.31159973;40.30217361;0.369984652
+-89.66110229;42.3512001;0.409056064
+-99.86830139;40.52560043;0.160280894
+-92.78015137;42.38292313;0.471072715
+-94.67822266;43.37779999;0.45208028
+-85.4654007;39.61999893;0.372370065
+-90.27745056;33.5304985;0.069951261
+-85.48919678;40.82904816;0.244631982
+-96.62359619;28.96854973;0.10605161
+-92.31777191;42.77460098;0.403202786
+-90.02518463;36.31344986;0.033505462
+-100.2697525;46.28807449;0.001241821
+-85.61994934;39.00154877;0.115980065
+-89.6453476;36.60385132;0.175375363
+-88.58305359;40.86539841;0.439816145
+-96.26529694;44.41397476;0.242072137
+-85.01129913;40.15707397;0.282942826
+-94.20709991;43.20404816;0.47211745
+-90.94364929;33.82437515;0.023893618
+-98.95137787;40.51995087;0.384243649
+-96.4693985;45.38150024;0.222607908
+-91.25454712;33.81832504;0.006127531
+-87.26167297;40.15950012;0.381453916
+-93.26055145;43.37742615;0.416015625
+-97.59490204;40.17627335;0.215385954
+-85.65450287;40.51599884;0.26379429
+-92.31772614;43.06029892;0.373100131
+-95.15070343;42.73529816;0.42679373
+-88.19625092;40.13975143;0.425889839
+-98.05400085;40.93600082;0.507249209
+-95.62445068;43.3777504;0.427094566
+-92.30919647;42.46974945;0.366441255
+-85.01129913;40.43930054;0.244369722
+-90.43624878;36.34980011;0.041549351
+-97.25032806;42.60874939;0.07978939
+-86.57279968;40.5842514;0.412178316
+-97.59680176;40.87279892;0.460612601
+-88.19560242;40.69805145;0.449552377
+-93.93090057;42.0366745;0.386892783
+-92.31707764;43.35675049;0.344911808
+-83.05825043;41.54014969;0.097445578
+-89.93524933;38.82717514;0.338917598
+-98.0508728;40.5244751;0.299940666
+-94.19860077;44.58684921;0.347458167
+-86.05670166;38.89854813;0.151859869
+-97.94225311;41.13367462;0.372514849
+-90.51979828;41.88140106;0.379956977
+-91.13140106;42.12142563;0.351712555
+-90.77375031;41.19862366;0.357329061
+-89.42150116;39.26160049;0.348776286
+-94.20722198;42.77685165;0.446007228
+-86.37335205;40.73600006;0.342495822
+-95.64129639;44.95280075;0.361133751
+-91.25557709;33.28395081;0.027900486
+-88.82309723;42.32365036;0.387194688
+-89.52185059;36.34429932;0.105173785
+-88.17402649;38.07529831;0.246908279
+-90.14689636;41.36687469;0.400869766
+-96.5059967;46.89082336;0.051872058
+-96.37825012;48.35832596;0.46896587
+-94.86074829;42.03635025;0.42403304
+-90.26055145;39.69842529;0.355213535
+-102.2617493;34.53065109;0.095839688
+-90.61444855;40.84859848;0.404362973
+-90.36295319;32.75875092;0.080378312
+-83.46130371;39.54712677;0.274392493
+-94.25565338;44.80490112;0.02472399
+-92.78865051;43.35639954;0.414849548
+-89.59384918;40.53390121;0.337370542
+-88.20545197;39.76599884;0.483150818
+-86.29299927;41.59659958;0.071399496
+-97.59784698;41.22079849;0.32685603
+-89.30979919;36.81890106;0.228800586
+-93.73435211;42.73292542;0.449334447
+-94.18710327;42.42702484;0.403060903
+-85.3967514;40.22785187;0.23797643
+-96.00012207;45.58620071;0.356945878
+-98.88269806;47.45712662;0.022630668
+-83.64785004;40.66189957;0.224263221
+-93.46487427;42.03620148;0.429587945
+-94.98954773;44.67425156;0.385172496
+-90.37457275;39.09270096;0.249299084
+-87.09030151;39.38764954;0.228070298
+-96.08394623;42.38647461;0.31873503
+-87.94407654;37.6908493;0.34546146
+-95.38134766;42.03715134;0.372929965
+-95.27850342;38.89844894;0.006498019
+-89.53884888;37.05530167;0.218967483
+-91.36945343;42.4708519;0.413710305
+-94.39659882;42.03654861;0.426078887
+-90.00295258;42.06399918;0.445313028
+-87.75019836;39.67969894;0.400428995
+-87.73445129;40.17969894;0.351748454
+-90.60714722;33.62872314;0.064861125
+-87.6520462;38.34925079;0.270443758
+-97.59619904;40.52457428;0.336645214
+-96.52729797;46.32619858;0.084630305
+-88.09764862;38.70949936;0.304135197
+-88.54119873;37.75355148;0.399002799
+-88.8615036;40.16579819;0.385142613
+-86.84020233;40.73667526;0.414716273
+-85.25499725;39.11159897;0.102915808
+-94.67835236;43.08212662;0.414298193
+-86.46900177;40.30455017;0.41540669
+-92.78929901;43.06000137;0.393309493
+-85.32382202;40.47269821;0.179815263
+-88.87625122;41.2784996;0.384939027
+-87.88880157;41.14630127;0.406815663
+-90.67449951;34.25445175;0.052445186
+-96.46205139;47.32529831;0.031917959
+-90.96665192;33.26884842;0.101434412
+-87.31015015;40.60630035;0.453010017
+-93.73374939;43.37752533;0.4642982
+-98.50095367;40.52460098;0.378276921
+-91.13152313;41.77225113;0.373423264
+-87.89015198;38.01462555;0.294905141
+-88.41835022;41.28544998;0.386624792
+-87.44117737;39.08039856;0.211384727
+-87.42769623;38.6609993;0.33471541
+-88.45290375;42.32415009;0.311389059
+-86.46829987;40.05170059;0.401294785
+-99.41124725;40.51815033;0.42594959
+-83.79194641;39.40819931;0.206873053
+-99.21514893;45.41854858;0.095884836
+-89.93804932;41.75759888;0.469363801
+-98.06659698;42.17625046;0.2001756
+-96.55410004;45.80369949;0.278701946
+-87.82859802;40.7478981;0.430081823
+-89.31324768;42.04505157;0.389374102
+-84.99884796;41.39709854;0.269223027
+-87.59944916;37.78969955;0.205909752
+-87.07777405;38.70209885;0.279367715
+-95.38839722;39.23210144;0.003205965
+-89.92569733;39.26065063;0.380446045
+-89.51270294;41.36677551;0.445780948
+-90.52909851;33.12594986;0.061290631
+-89.24485016;40.76100159;0.398356852
+-91.83944702;42.47019958;0.421218009
+-98.07407379;46.93562317;0.034776925
+-89.31414795;41.21319962;0.339291302
+-93.71707153;42.38375092;0.451127575
+-90.84234619;34.38195038;0.045115279
+-90.67727661;40.45764923;0.396196513
+-92.78967285;42.7317009;0.378254333
+-88.64040375;39.61975098;0.401799017
+-93.73435211;43.08169937;0.435008142
+-93.25002289;42.3830986;0.441911429
+-93.26065063;43.08160019;0.427513504
+-88.60269928;40.03654861;0.4396107
+-89.37345123;40.12097549;0.455246789
+-93.26174927;42.73244858;0.439959481
+-89.27935028;39.58472443;0.413840155
+-96.01200104;45.93434906;0.215325946
+-89.81130219;41.10385132;0.489468292
+-99.99237823;45.07220078;0.002704878
+-89.28507233;41.74874878;0.452146176
+-97.15119934;47.45484924;0.113304772
+-95.26959991;39.53597641;0.003252648
+-98.40887451;43.34777451;0.005766614
+-97.12895203;44.02209854;0.003606431
+-95.09937286;39.81169891;0.011554006
diff --git a/InteractiveDataDisplay_OneDll/samples/BingMapsSample/packages.config b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/packages.config
new file mode 100644
index 000000000..10664af48
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/BingMapsSample/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/InteractiveDataDisplay_OneDll/samples/HeatmapSample/App.config b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/App.config
new file mode 100644
index 000000000..88fa4027b
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/InteractiveDataDisplay_OneDll/samples/HeatmapSample/App.xaml b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/App.xaml
new file mode 100644
index 000000000..158681e54
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/InteractiveDataDisplay_OneDll/samples/HeatmapSample/App.xaml.cs b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/App.xaml.cs
new file mode 100644
index 000000000..84802cb85
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/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 HeatmapSample
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/InteractiveDataDisplay_OneDll/samples/HeatmapSample/HeatmapSample.csproj b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/HeatmapSample.csproj
new file mode 100644
index 000000000..efd14c70b
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/HeatmapSample.csproj
@@ -0,0 +1,120 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {A59C204A-01D9-41A4-BE67-64ECDB991560}
+ WinExe
+ HeatmapSample
+ HeatmapSample
+ v4.5.2
+ 512
+ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 4
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+ ..\packages\System.Reactive.Core.3.1.1\lib\net45\System.Reactive.Core.dll
+
+
+ ..\packages\System.Reactive.Interfaces.3.1.1\lib\net45\System.Reactive.Interfaces.dll
+
+
+ ..\packages\System.Reactive.Linq.3.1.1\lib\net45\System.Reactive.Linq.dll
+
+
+ ..\packages\System.Reactive.PlatformServices.3.1.1\lib\net45\System.Reactive.PlatformServices.dll
+
+
+ ..\packages\System.Reactive.Windows.Threading.3.1.1\lib\net45\System.Reactive.Windows.Threading.dll
+
+
+
+
+
+
+
+
+
+ 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
+
+
+
+
+
+
+
+ {2d34544c-2df2-4b20-a43a-6c8d2df3dd82}
+ InteractiveDataDisplay.WPF
+
+
+
+
\ No newline at end of file
diff --git a/InteractiveDataDisplay_OneDll/samples/HeatmapSample/MainWindow.xaml b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/MainWindow.xaml
new file mode 100644
index 000000000..8539554c4
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/MainWindow.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+ Heatmap sample
+
+
+
+
+
diff --git a/InteractiveDataDisplay_OneDll/samples/HeatmapSample/MainWindow.xaml.cs b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/MainWindow.xaml.cs
new file mode 100644
index 000000000..89144ae67
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/MainWindow.xaml.cs
@@ -0,0 +1,97 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Linq;
+using System.Threading;
+using System.Windows;
+using System.Reactive.Linq;
+
+namespace HeatmapSample
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ private volatile bool isUnloaded = false;
+ private AutoResetEvent renderComplete = new AutoResetEvent(false);
+ private Thread thread;
+ private double phase = 0;
+
+ const int N = 1000;
+ const int M = 500;
+
+ double[] x = new double[N + 1];
+ double[] y = new double[M + 1];
+ double[,] f = new double[N, M];
+
+ public MainWindow()
+ {
+ InitializeComponent();
+
+ // Start computation thread when Page appears on the screen
+ Loaded += (s, e) =>
+ {
+ thread = new Thread(ModelRun);
+ isUnloaded = false;
+ thread.Start();
+ };
+
+ // Stop computation thread when Page is removed from the screen
+ Closing += (s, e) =>
+ {
+ isUnloaded = true;
+ renderComplete.Set();
+ thread.Join();
+ };
+ }
+
+ private void ModelRun()
+ {
+ // Coordinate grid is constant
+ for (int i = 0; i <= N; i++)
+ x[i] = -Math.PI + 2 * i * Math.PI / N;
+
+ for (int j = 0; j <= M; j++)
+ y[j] = -Math.PI / 2 + j * Math.PI / M;
+
+ while (!isUnloaded) // Run until page is on the screen
+ {
+ // Data array is updated
+ for (int i = 0; i < N; i++)
+ for (int j = 0; j < M; j++)
+ f[i, j] = Math.Sqrt(x[i] * x[i] + y[j] * y[j]) * Math.Abs(Math.Cos(x[i] * x[i] + y[j] * y[j] + phase));
+ phase += 0.1;
+
+ // Uncomment next line to simulate delay in computations
+ // Thread.Sleep(1000);
+
+ // Plot the computed array.
+ // The Plot method must be called from the UI dispatcher thread.
+ // Unlike Tutoral3 sample here we wait for each frame to be rendered.
+ if (!isUnloaded)
+ {
+ renderComplete.Reset();
+ PlotDataDel handler = PlotData;
+ Dispatcher.BeginInvoke(handler);
+ renderComplete.WaitOne();
+ }
+ }
+ }
+ private delegate void PlotDataDel();
+ private void PlotData()
+ {
+ // HeatmapGraph objects prepare images to be drawn in a background thread.
+ // The Plot method cancels current incomplete images before starting a new one.
+ // This increases responsiveness of the UI but may result in loss of certain
+ // or even all of the frames.
+ // The following code shows how to wait until a certain data is actually drawn.
+
+ long id = heatmap.Plot(f, x, y); // receive a unique operation identifier
+ heatmap.RenderCompletion // an observable of completed and cancelled operations
+ .Where(rc => rc.TaskId == id) // filter out an operation with the known id
+ .Subscribe(dummy => { renderComplete.Set(); }); // signal when the id is observed
+ }
+ }
+}
diff --git a/InteractiveDataDisplay_OneDll/samples/HeatmapSample/Properties/AssemblyInfo.cs b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..13a33f81a
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/Properties/AssemblyInfo.cs
@@ -0,0 +1,55 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("HeatmapSample")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("HeatmapSample")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+//In order to begin building localizable applications, set
+//CultureYouAreCodingWith in your .csproj file
+//inside a . For example, if you are using US english
+//in your source files, set the to en-US. Then uncomment
+//the NeutralResourceLanguage attribute below. Update the "en-US" in
+//the line below to match the UICulture setting in the project file.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[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)
+)]
+
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/InteractiveDataDisplay_OneDll/samples/HeatmapSample/Properties/Resources.Designer.cs b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..d154f716c
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/Properties/Resources.Designer.cs
@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+//
+// 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 HeatmapSample.Properties
+{
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [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()
+ {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [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("HeatmapSample.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [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/InteractiveDataDisplay_OneDll/samples/HeatmapSample/Properties/Resources.resx b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/Properties/Resources.resx
new file mode 100644
index 000000000..af7dbebba
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/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/InteractiveDataDisplay_OneDll/samples/HeatmapSample/Properties/Settings.Designer.cs b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..bcbf719d2
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+//
+// 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 HeatmapSample.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/InteractiveDataDisplay_OneDll/samples/HeatmapSample/Properties/Settings.settings b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/Properties/Settings.settings
new file mode 100644
index 000000000..033d7a5e9
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/InteractiveDataDisplay_OneDll/samples/HeatmapSample/packages.config b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/packages.config
new file mode 100644
index 000000000..ae4ab0a2d
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/HeatmapSample/packages.config
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/InteractiveDataDisplay_OneDll/samples/LineGraphSample/App.config b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/App.config
new file mode 100644
index 000000000..88fa4027b
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/InteractiveDataDisplay_OneDll/samples/LineGraphSample/App.xaml b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/App.xaml
new file mode 100644
index 000000000..ac43cbb64
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/InteractiveDataDisplay_OneDll/samples/LineGraphSample/App.xaml.cs b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/App.xaml.cs
new file mode 100644
index 000000000..43804b1d4
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/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 LineGraphSample
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/InteractiveDataDisplay_OneDll/samples/LineGraphSample/LineGraphSample.csproj b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/LineGraphSample.csproj
new file mode 100644
index 000000000..f4d1ea65c
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/LineGraphSample.csproj
@@ -0,0 +1,103 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {284A7617-B70F-4A4A-B73A-1A8088B75926}
+ WinExe
+ LineGraphSample
+ LineGraphSample
+ v4.5.2
+ 512
+ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 4
+ 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
+
+
+
+
+
+
+
+ {2d34544c-2df2-4b20-a43a-6c8d2df3dd82}
+ InteractiveDataDisplay.WPF
+
+
+
+
\ No newline at end of file
diff --git a/InteractiveDataDisplay_OneDll/samples/LineGraphSample/MainWindow.xaml b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/MainWindow.xaml
new file mode 100644
index 000000000..b3082e40d
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/MainWindow.xaml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+ Line graph legend sample
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/InteractiveDataDisplay_OneDll/samples/LineGraphSample/MainWindow.xaml.cs b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/MainWindow.xaml.cs
new file mode 100644
index 000000000..9e05c7bef
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/MainWindow.xaml.cs
@@ -0,0 +1,50 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Linq;
+using System.Windows;
+using System.Windows.Data;
+using System.Windows.Media;
+using InteractiveDataDisplay.WPF;
+
+namespace LineGraphSample
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+
+ double[] x = new double[200];
+ for (int i = 0; i < x.Length; i++)
+ x[i] = 3.1415 * i / (x.Length - 1);
+
+ for (int i = 0; i < 25; i++)
+ {
+ var lg = new LineGraph();
+ lines.Children.Add(lg);
+ lg.Stroke = new SolidColorBrush(Color.FromArgb(255, 0, (byte)(i * 10), 0));
+ lg.Description = String.Format("Data series {0}", i + 1);
+ lg.StrokeThickness = 2;
+ lg.Plot(x, x.Select(v => Math.Sin(v + i / 10.0)).ToArray());
+ }
+ }
+ }
+
+ public class VisibilityToCheckedConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+ {
+ return ((Visibility)value) == Visibility.Visible;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+ {
+ return ((bool)value) ? Visibility.Visible : Visibility.Collapsed;
+ }
+ }
+}
diff --git a/InteractiveDataDisplay_OneDll/samples/LineGraphSample/Properties/AssemblyInfo.cs b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..c9d23ca9c
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/Properties/AssemblyInfo.cs
@@ -0,0 +1,55 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("LineGraphSample")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("LineGraphSample")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+//In order to begin building localizable applications, set
+//CultureYouAreCodingWith in your .csproj file
+//inside a . For example, if you are using US english
+//in your source files, set the to en-US. Then uncomment
+//the NeutralResourceLanguage attribute below. Update the "en-US" in
+//the line below to match the UICulture setting in the project file.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[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)
+)]
+
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/InteractiveDataDisplay_OneDll/samples/LineGraphSample/Properties/Resources.Designer.cs b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..9b9dc79ba
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/Properties/Resources.Designer.cs
@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+//
+// 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 LineGraphSample.Properties
+{
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [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()
+ {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [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("LineGraphSample.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [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/InteractiveDataDisplay_OneDll/samples/LineGraphSample/Properties/Resources.resx b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/Properties/Resources.resx
new file mode 100644
index 000000000..af7dbebba
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/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/InteractiveDataDisplay_OneDll/samples/LineGraphSample/Properties/Settings.Designer.cs b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..d0381d518
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+//
+// 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 LineGraphSample.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/InteractiveDataDisplay_OneDll/samples/LineGraphSample/Properties/Settings.settings b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/Properties/Settings.settings
new file mode 100644
index 000000000..033d7a5e9
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/LineGraphSample/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/InteractiveDataDisplay_OneDll/samples/MarkersSample/App.config b/InteractiveDataDisplay_OneDll/samples/MarkersSample/App.config
new file mode 100644
index 000000000..88fa4027b
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/MarkersSample/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/InteractiveDataDisplay_OneDll/samples/MarkersSample/App.xaml b/InteractiveDataDisplay_OneDll/samples/MarkersSample/App.xaml
new file mode 100644
index 000000000..65a1fe8e6
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/MarkersSample/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/InteractiveDataDisplay_OneDll/samples/MarkersSample/App.xaml.cs b/InteractiveDataDisplay_OneDll/samples/MarkersSample/App.xaml.cs
new file mode 100644
index 000000000..34d1954b0
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/MarkersSample/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 MarkersSample
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/InteractiveDataDisplay_OneDll/samples/MarkersSample/MainWindow.xaml b/InteractiveDataDisplay_OneDll/samples/MarkersSample/MainWindow.xaml
new file mode 100644
index 000000000..011b8a519
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/MarkersSample/MainWindow.xaml
@@ -0,0 +1,18 @@
+
+
+
+
+ Markers sample
+
+
+
+
+
+
+
+
diff --git a/InteractiveDataDisplay_OneDll/samples/MarkersSample/MainWindow.xaml.cs b/InteractiveDataDisplay_OneDll/samples/MarkersSample/MainWindow.xaml.cs
new file mode 100644
index 000000000..700505fcc
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/MarkersSample/MainWindow.xaml.cs
@@ -0,0 +1,52 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Windows;
+
+namespace MarkersSample
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+
+ const int N =100;
+ const int M = 1000;
+ double[] x = new double[N];
+ double[] y = new double[N];
+ double[] c = new double[N];
+ double[] d = new double[N];
+
+ Random rand = new Random();
+ //circles
+ for (int i = 0; i < N; i++)
+ {
+ x[i] = rand.Next(M);
+ y[i] = rand.Next(M);
+ c[i] = rand.NextDouble();
+ d[i] = 20 * rand.NextDouble();
+ }
+
+ circles.PlotColorSize(x, y, c, d);
+ //diamonds
+ x = new double[N];
+ y = new double[N];
+ c = new double[N];
+ d = new double[N];
+
+ for (int i = 0; i < N; i++)
+ {
+ x[i] = rand.Next(M);
+ y[i] = rand.Next(M);
+ c[i] = rand.NextDouble();
+ d[i] = 20 * rand.NextDouble();
+ }
+ diamonds.PlotColorSize(x, y, c, d);
+ }
+ }
+}
diff --git a/InteractiveDataDisplay_OneDll/samples/MarkersSample/MarkersSample.csproj b/InteractiveDataDisplay_OneDll/samples/MarkersSample/MarkersSample.csproj
new file mode 100644
index 000000000..9c5f8bc73
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/MarkersSample/MarkersSample.csproj
@@ -0,0 +1,103 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {4BEA4B4C-5B83-4FD7-B9BF-3AE4F1CC571F}
+ WinExe
+ MarkersSample
+ MarkersSample
+ v4.5.2
+ 512
+ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 4
+ 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
+
+
+
+
+
+
+
+ {2d34544c-2df2-4b20-a43a-6c8d2df3dd82}
+ InteractiveDataDisplay.WPF
+
+
+
+
\ No newline at end of file
diff --git a/InteractiveDataDisplay_OneDll/samples/MarkersSample/Properties/AssemblyInfo.cs b/InteractiveDataDisplay_OneDll/samples/MarkersSample/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..b7bcd4ffa
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/MarkersSample/Properties/AssemblyInfo.cs
@@ -0,0 +1,55 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("MarkersSample")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("MarkersSample")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+//In order to begin building localizable applications, set
+//CultureYouAreCodingWith in your .csproj file
+//inside a . For example, if you are using US english
+//in your source files, set the to en-US. Then uncomment
+//the NeutralResourceLanguage attribute below. Update the "en-US" in
+//the line below to match the UICulture setting in the project file.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[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)
+)]
+
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/InteractiveDataDisplay_OneDll/samples/MarkersSample/Properties/Resources.Designer.cs b/InteractiveDataDisplay_OneDll/samples/MarkersSample/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..8b84e16b0
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/MarkersSample/Properties/Resources.Designer.cs
@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+//
+// 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 MarkersSample.Properties
+{
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [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()
+ {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [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("MarkersSample.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [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/InteractiveDataDisplay_OneDll/samples/MarkersSample/Properties/Resources.resx b/InteractiveDataDisplay_OneDll/samples/MarkersSample/Properties/Resources.resx
new file mode 100644
index 000000000..af7dbebba
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/MarkersSample/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/InteractiveDataDisplay_OneDll/samples/MarkersSample/Properties/Settings.Designer.cs b/InteractiveDataDisplay_OneDll/samples/MarkersSample/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..b602d2628
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/MarkersSample/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+//
+// 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 MarkersSample.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/InteractiveDataDisplay_OneDll/samples/MarkersSample/Properties/Settings.settings b/InteractiveDataDisplay_OneDll/samples/MarkersSample/Properties/Settings.settings
new file mode 100644
index 000000000..033d7a5e9
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/MarkersSample/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/App.config b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/App.config
new file mode 100644
index 000000000..8227adb98
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/App.xaml b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/App.xaml
new file mode 100644
index 000000000..cf49a1a7c
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/App.xaml.cs b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/App.xaml.cs
new file mode 100644
index 000000000..baf17bc01
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/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 SyncGraphsSample
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/MainWindow.xaml b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/MainWindow.xaml
new file mode 100644
index 000000000..e74aa46d9
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/MainWindow.xaml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ These three Charts have shared axes. Two of the share
+ vertical axis, two - horizontal. Navigate in one plot and see how other is changed. This is done purely in
+ XAML.
+
+
+
diff --git a/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/MainWindow.xaml.cs b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/MainWindow.xaml.cs
new file mode 100644
index 000000000..e023808ac
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/MainWindow.xaml.cs
@@ -0,0 +1,40 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Linq;
+using System.Windows;
+using System.Windows.Media;
+
+namespace SyncGraphsSample
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+
+ barGraph.PlotBars(Enumerable.Range(1, 10));
+
+ int N = 25;
+ Random r = new Random();
+ double[] x = new double[N];
+ double[] y = new double[N];
+ for (int i = 0; i < N; i++)
+ {
+ x[i] = r.NextDouble() * 12 - 1;
+ y[i] = r.NextDouble() * 12 - 1;
+ }
+ circles.PlotXY(x, y);
+
+ double[] x1 = Enumerable.Range(0, 90).Select(i => i / 10.0).ToArray();
+ double[] y1 = x1.Select(v => 7 * (Math.Abs(v) < 1e-10 ? 1 : Math.Sin(v) / v) + 1).ToArray();
+ lineGraph1.Stroke = new SolidColorBrush(Colors.Green);
+ lineGraph1.StrokeThickness = 2.0;
+ lineGraph1.Plot(x1, y1);
+ }
+ }
+}
diff --git a/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/Properties/AssemblyInfo.cs b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..e1d9baa18
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/Properties/AssemblyInfo.cs
@@ -0,0 +1,55 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("SyncGraphsSample")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("SyncGraphsSample")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+//In order to begin building localizable applications, set
+//CultureYouAreCodingWith in your .csproj file
+//inside a . For example, if you are using US english
+//in your source files, set the to en-US. Then uncomment
+//the NeutralResourceLanguage attribute below. Update the "en-US" in
+//the line below to match the UICulture setting in the project file.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[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)
+)]
+
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/Properties/Resources.Designer.cs b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..30ad0cd71
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/Properties/Resources.Designer.cs
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+//
+// Этот код создан программой.
+// Исполняемая версия:4.0.30319.42000
+//
+// Изменения в этом файле могут привести к неправильной работе и будут потеряны в случае
+// повторной генерации кода.
+//
+//------------------------------------------------------------------------------
+
+namespace SyncGraphsSample.Properties {
+ using System;
+
+
+ ///
+ /// Класс ресурса со строгой типизацией для поиска локализованных строк и т.д.
+ ///
+ // Этот класс создан автоматически классом StronglyTypedResourceBuilder
+ // с помощью такого средства, как ResGen или Visual Studio.
+ // Чтобы добавить или удалить член, измените файл .ResX и снова запустите ResGen
+ // с параметром /str или перестройте свой проект VS.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.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 (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SyncGraphsSample.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/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/Properties/Resources.resx b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/Properties/Resources.resx
new file mode 100644
index 000000000..af7dbebba
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/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/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/Properties/Settings.Designer.cs b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/Properties/Settings.Designer.cs
new file mode 100644
index 000000000..c51c31c4b
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/Properties/Settings.Designer.cs
@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+//
+// Этот код создан программой.
+// Исполняемая версия:4.0.30319.42000
+//
+// Изменения в этом файле могут привести к неправильной работе и будут потеряны в случае
+// повторной генерации кода.
+//
+//------------------------------------------------------------------------------
+
+namespace SyncGraphsSample.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.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/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/Properties/Settings.settings b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/Properties/Settings.settings
new file mode 100644
index 000000000..033d7a5e9
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/SyncGraphsSample.csproj b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/SyncGraphsSample.csproj
new file mode 100644
index 000000000..a0886badb
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/samples/SyncGraphsSample/SyncGraphsSample.csproj
@@ -0,0 +1,113 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {0C4C6C54-F253-4FD2-8B38-75E476882A4B}
+ WinExe
+ Properties
+ SyncGraphsSample
+ SyncGraphsSample
+ v4.5.2
+ 512
+ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 4
+ 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
+
+
+
+
+
+
+
+
+ {2d34544c-2df2-4b20-a43a-6c8d2df3dd82}
+ InteractiveDataDisplay.WPF
+
+
+
+
+
\ No newline at end of file
diff --git a/InteractiveDataDisplay_OneDll/sln/InteractiveDataDisplay.WPF.sln b/InteractiveDataDisplay_OneDll/sln/InteractiveDataDisplay.WPF.sln
new file mode 100644
index 000000000..ba1327086
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/sln/InteractiveDataDisplay.WPF.sln
@@ -0,0 +1,57 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31313.79
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InteractiveDataDisplay.WPF", "..\src\InteractiveDataDisplay.WPF.csproj", "{2D34544C-2DF2-4B20-A43A-6C8D2DF3DD82}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{6E637A98-DA68-40CF-B194-56CEB99A57A0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MarkersSample", "..\samples\MarkersSample\MarkersSample.csproj", "{4BEA4B4C-5B83-4FD7-B9BF-3AE4F1CC571F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LineGraphSample", "..\samples\LineGraphSample\LineGraphSample.csproj", "{284A7617-B70F-4A4A-B73A-1A8088B75926}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BarChartSample", "..\samples\BarChartSample\BarChartSample.csproj", "{F36872BF-8818-4019-A207-2B381C33C370}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyncGraphsSample", "..\samples\SyncGraphsSample\SyncGraphsSample.csproj", "{0C4C6C54-F253-4FD2-8B38-75E476882A4B}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {2D34544C-2DF2-4B20-A43A-6C8D2DF3DD82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2D34544C-2DF2-4B20-A43A-6C8D2DF3DD82}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2D34544C-2DF2-4B20-A43A-6C8D2DF3DD82}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2D34544C-2DF2-4B20-A43A-6C8D2DF3DD82}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4BEA4B4C-5B83-4FD7-B9BF-3AE4F1CC571F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4BEA4B4C-5B83-4FD7-B9BF-3AE4F1CC571F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4BEA4B4C-5B83-4FD7-B9BF-3AE4F1CC571F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4BEA4B4C-5B83-4FD7-B9BF-3AE4F1CC571F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {284A7617-B70F-4A4A-B73A-1A8088B75926}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {284A7617-B70F-4A4A-B73A-1A8088B75926}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {284A7617-B70F-4A4A-B73A-1A8088B75926}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {284A7617-B70F-4A4A-B73A-1A8088B75926}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F36872BF-8818-4019-A207-2B381C33C370}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F36872BF-8818-4019-A207-2B381C33C370}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F36872BF-8818-4019-A207-2B381C33C370}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F36872BF-8818-4019-A207-2B381C33C370}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0C4C6C54-F253-4FD2-8B38-75E476882A4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0C4C6C54-F253-4FD2-8B38-75E476882A4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0C4C6C54-F253-4FD2-8B38-75E476882A4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0C4C6C54-F253-4FD2-8B38-75E476882A4B}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {4BEA4B4C-5B83-4FD7-B9BF-3AE4F1CC571F} = {6E637A98-DA68-40CF-B194-56CEB99A57A0}
+ {284A7617-B70F-4A4A-B73A-1A8088B75926} = {6E637A98-DA68-40CF-B194-56CEB99A57A0}
+ {F36872BF-8818-4019-A207-2B381C33C370} = {6E637A98-DA68-40CF-B194-56CEB99A57A0}
+ {0C4C6C54-F253-4FD2-8B38-75E476882A4B} = {6E637A98-DA68-40CF-B194-56CEB99A57A0}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {206EA3D1-9D79-4D18-BE4A-C941D2C4BDEF}
+ EndGlobalSection
+EndGlobal
diff --git a/InteractiveDataDisplay_OneDll/src/Axes/Axis.cs b/InteractiveDataDisplay_OneDll/src/Axes/Axis.cs
new file mode 100644
index 000000000..a0cfd2dcd
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Axes/Axis.cs
@@ -0,0 +1,656 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Shapes;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Data;
+using System.ComponentModel;
+
+namespace InteractiveDataDisplay.WPF
+{
+ /// Facilitates drawing of vertical or horizontal coordinate axis
+ [Description("Vertical or horizontal coordinate axis")]
+ public class Axis : Panel
+ {
+ private ILabelProvider labelProvider;
+ private TicksProvider ticksProvider;
+ private Path majorTicksPath;
+ private Path minorTicksPath;
+
+ private FrameworkElement[] labels;
+
+ private const int maxTickArrangeIterations = 12;
+ private int maxTicks = 20;
+ private const double increaseRatio = 8.0;
+ private const double decreaseRatio = 8.0;
+ private const double tickLength = 10;
+
+ private bool drawTicks = true;
+ private bool drawMinorTicks = true;
+
+ ///
+ /// Initializes a new instance of class with double
+ /// and .
+ ///
+ public Axis()
+ {
+ DataTransform = new IdentityDataTransform();
+ Ticks = new double[0];
+
+ majorTicksPath = new Path();
+ minorTicksPath = new Path();
+ Children.Add(majorTicksPath);
+ Children.Add(minorTicksPath);
+
+ BindingOperations.SetBinding(majorTicksPath, Path.StrokeProperty, new Binding("Foreground") { Source = this, Mode = BindingMode.TwoWay });
+ BindingOperations.SetBinding(minorTicksPath, Path.StrokeProperty, new Binding("Foreground") { Source = this, Mode = BindingMode.TwoWay });
+
+ if (labelProvider == null)
+ this.labelProvider = new LabelProvider();
+ if (ticksProvider == null)
+ this.ticksProvider = new TicksProvider();
+ }
+
+ ///
+ /// Initializes a new instance of class.
+ ///
+ /// A to create labels.
+ /// A to create ticks.
+ public Axis(ILabelProvider labelProvider, TicksProvider ticksProvider)
+ : this()
+ {
+ this.labelProvider = labelProvider;
+ this.ticksProvider = ticksProvider;
+ }
+
+ ///
+ /// Gets or sets a set of double values displayed as axis ticks in data values.
+ ///
+ ///
+ /// Ticks are calculated from current Range of axis in plot coordinates and current DataTransform of axis.
+ /// Example: for range [-1,1] int plot coordinates and DataTransform as y = 0.001 * x + 0.5 values of Ticks will be in [-1000,1000] range
+ /// It is not recommended to set this property manually.
+ ///
+ [Browsable(false)]
+ public IEnumerable Ticks
+ {
+ get { return (IEnumerable)GetValue(TicksProperty); }
+ set { SetValue(TicksProperty, value); }
+ }
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty TicksProperty =
+ DependencyProperty.Register("Ticks", typeof(IEnumerable), typeof(Axis), new PropertyMetadata(new double[0]));
+
+ ///
+ /// Gets or sets the range of values on axis in plot coordinates.
+ ///
+ ///
+ /// should be inside Domain of , otherwise exception will be thrown.
+ ///
+ [Category("InteractiveDataDisplay")]
+ [Description("Range of values on axis")]
+ public Range Range
+ {
+ get { return (Range)GetValue(RangeProperty); }
+ set { SetValue(RangeProperty, value); }
+ }
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty RangeProperty =
+ DependencyProperty.Register("Range", typeof(Range), typeof(Axis), new PropertyMetadata(new Range(0, 1),
+ (o, e) =>
+ {
+ Axis axis = (Axis)o;
+ if (axis != null)
+ {
+ axis.InvalidateMeasure();
+ }
+ }));
+
+
+ ///
+ /// Gets or sets orientation of the axis and location of labels
+ ///
+ /// The default AxisOrientation is AxisOrientation.Bottom
+ [Category("InteractiveDataDisplay")]
+ [Description("Defines orientation of axis and location of labels")]
+ public AxisOrientation AxisOrientation
+ {
+ get { return (AxisOrientation)GetValue(AxisOrientationProperty); }
+ set { SetValue(AxisOrientationProperty, value); }
+ }
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty AxisOrientationProperty =
+ DependencyProperty.Register("AxisOrientation", typeof(AxisOrientation), typeof(Axis), new PropertyMetadata(AxisOrientation.Bottom,
+ (o, e) =>
+ {
+ Axis axis = (Axis)o;
+ if (axis != null)
+ {
+ axis.InvalidateMeasure();
+ }
+ }));
+
+ ///
+ /// Gets or sets a flag indicating whether the axis is reversed or not.
+ ///
+ /// Axis is not reversed by default.
+ [Category("InteractiveDataDisplay")]
+ [Description("Defines orientation of axis and location of labels")]
+ public bool IsReversed
+ {
+ get { return (bool)GetValue(IsReversedProperty); }
+ set { SetValue(IsReversedProperty, value); }
+ }
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty IsReversedProperty =
+ DependencyProperty.Register("IsReversed", typeof(bool), typeof(Axis), new PropertyMetadata(false,
+ (o, e) =>
+ {
+ Axis axis = (Axis)o;
+ if (axis != null)
+ {
+ axis.InvalidateMeasure();
+ }
+ }));
+
+ ///
+ /// Gets or sets for an axis.
+ ///
+ ///
+ /// The default transform is
+ /// DataTransform is used for transform plot coordinates from Range property to data values, which will be displayed on ticks
+ ///
+ [Description("Transform from data to plot coordinates")]
+ [Category("InteractiveDataDisplay")]
+ public DataTransform DataTransform
+ {
+ get { return (DataTransform)GetValue(DataTransformProperty); }
+ set { SetValue(DataTransformProperty, value); }
+ }
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty DataTransformProperty =
+ DependencyProperty.Register("DataTransform", typeof(DataTransform), typeof(Axis), new PropertyMetadata(null,
+ (o, e) =>
+ {
+ Axis axis = (Axis)o;
+ if (axis != null)
+ {
+ axis.InvalidateMeasure();
+ }
+ }));
+
+ ///
+ /// Gets or sets the brush for labels and ticks of axis
+ ///
+ /// The default foreground is black
+ [Category("Appearance")]
+ [Description("Brush for labels and ticks")]
+ public SolidColorBrush Foreground
+ {
+ get { return (SolidColorBrush)GetValue(ForegroundProperty); }
+ set { SetValue(ForegroundProperty, value); }
+ }
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty ForegroundProperty =
+ DependencyProperty.Register("Foreground", typeof(SolidColorBrush), typeof(Axis), new PropertyMetadata(new SolidColorBrush(Colors.Black)));
+
+ ///
+ /// Gets or sets the maximum possible count of ticks.
+ ///
+ /// The defalut count is 20
+ [Category("InteractiveDataDisplay")]
+ [Description("Maximum number of major ticks")]
+ public int MaxTicks
+ {
+ set
+ {
+ maxTicks = value;
+ InvalidateMeasure();
+ }
+ get
+ {
+ return maxTicks;
+ }
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the ticks should be rendered or not.
+ ///
+ /// The default value is true.
+ [Category("InteractiveDataDisplay")]
+ public bool AreTicksVisible
+ {
+ set
+ {
+ drawTicks = value;
+ drawMinorTicks = value;
+ if (drawTicks)
+ InvalidateMeasure();
+ }
+ get { return drawTicks; }
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the minor ticks should be rendered or not.
+ ///
+ /// The default value is true.
+ [Category("InteractiveDataDisplay")]
+ public bool AreMinorTicksVisible
+ {
+ set
+ {
+ drawMinorTicks = value;
+ if (drawMinorTicks)
+ InvalidateMeasure();
+ }
+ get { return drawMinorTicks; }
+ }
+
+ private void CreateTicks(Size axisSize)
+ {
+ Range range = new Range(
+ DataTransform.PlotToData(Double.IsInfinity(Range.Min) || Double.IsNaN(Range.Min) ? 0 : Range.Min),
+ DataTransform.PlotToData(Double.IsInfinity(Range.Max) || Double.IsNaN(Range.Max) ? 1 : Range.Max));
+
+ // One tick if range is point
+ if (range.IsPoint)
+ {
+ var t = new double[] { range.Min };
+ Ticks = t;
+ labels = labelProvider.GetLabels(t);
+ return;
+ }
+
+ // Do first pass of ticks arrangement
+ ticksProvider.Range = range;
+ double[] ticks = ticksProvider.GetTicks();
+ labels = labelProvider.GetLabels(ticks);
+
+ TickChange result;
+ if (ticks.Length > MaxTicks)
+ result = TickChange.Decrease;
+ else if (ticks.Length < 2)
+ result = TickChange.Increase;
+ else
+ result = CheckLabelsArrangement(axisSize, labels, ticks);
+
+ int iterations = 0;
+ int prevLength = ticks.Length;
+ while (result != TickChange.OK && iterations++ < maxTickArrangeIterations)
+ {
+ if (result == TickChange.Increase)
+ ticksProvider.IncreaseTickCount();
+ else
+ ticksProvider.DecreaseTickCount();
+ double[] newTicks = ticksProvider.GetTicks();
+ if (newTicks.Length > MaxTicks && result == TickChange.Increase)
+ {
+ ticksProvider.DecreaseTickCount(); // Step back and stop to not get more than MaxTicks
+ break;
+ }
+ else if (newTicks.Length < 2 && result == TickChange.Decrease)
+ {
+ ticksProvider.IncreaseTickCount(); // Step back and stop to not get less than 2
+ break;
+ }
+ var prevTicks = ticks;
+ ticks = newTicks;
+ var prevLabels = labels;
+ labels = labelProvider.GetLabels(newTicks);
+ var newResult = CheckLabelsArrangement(axisSize, labels, ticks);
+ if (newResult == result) // Continue in the same direction
+ {
+ prevLength = newTicks.Length;
+ }
+ else // Direction changed or layout OK - stop the loop
+ {
+ if (newResult != TickChange.OK) // Direction changed - time to stop
+ {
+ if (result == TickChange.Decrease)
+ {
+ if (prevLength < MaxTicks)
+ {
+ ticks = prevTicks;
+ labels = prevLabels;
+ ticksProvider.IncreaseTickCount();
+ }
+ }
+ else
+ {
+ if (prevLength >= 2)
+ {
+ ticks = prevTicks;
+ labels = prevLabels;
+ ticksProvider.DecreaseTickCount();
+ }
+ }
+ break;
+ }
+ break;
+ }
+ }
+
+ Ticks = ticks;
+ }
+
+ private void DrawCanvas(Size axisSize, double[] cTicks)
+ {
+ double length = IsHorizontal ? axisSize.Width : axisSize.Height;
+
+ GeometryGroup majorTicksGeometry = new GeometryGroup();
+ GeometryGroup minorTicksGeometry = new GeometryGroup();
+
+ if (!Double.IsNaN(length) && length != 0)
+ {
+ int start = 0;
+ if (cTicks.Length > 0 && cTicks[0] < Range.Min) start++;
+
+ if (Range.IsPoint)
+ drawMinorTicks = false;
+
+ for (int i = start; i < cTicks.Length; i++)
+ {
+ LineGeometry line = new LineGeometry();
+ majorTicksGeometry.Children.Add(line);
+
+ if (IsHorizontal)
+ {
+ line.StartPoint = new Point(GetCoordinateFromTick(cTicks[i], axisSize), 0);
+ line.EndPoint = new Point(GetCoordinateFromTick(cTicks[i], axisSize), tickLength);
+ }
+ else
+ {
+ line.StartPoint = new Point(0, GetCoordinateFromTick(cTicks[i], axisSize));
+ line.EndPoint = new Point(tickLength, GetCoordinateFromTick(cTicks[i], axisSize));
+ }
+
+ if (labels[i] is TextBlock)
+ {
+ (labels[i] as TextBlock).Foreground = Foreground;
+ }
+ else if (labels[i] is Control)
+ {
+ (labels[i] as Control).Foreground = Foreground;
+ }
+
+ Children.Add(labels[i]);
+ }
+
+ if (drawMinorTicks)
+ {
+ double[] minorTicks = ticksProvider.GetMinorTicks(Range);
+ if (minorTicks != null)
+ {
+ for (int j = 0; j < minorTicks.Length; j++)
+ {
+ LineGeometry line = new LineGeometry();
+ minorTicksGeometry.Children.Add(line);
+
+ if (IsHorizontal)
+ {
+ line.StartPoint = new Point(GetCoordinateFromTick(minorTicks[j], axisSize), 0);
+ line.EndPoint = new Point(GetCoordinateFromTick(minorTicks[j], axisSize), tickLength / 2.0);
+ }
+ else
+ {
+ line.StartPoint = new Point(0, GetCoordinateFromTick(minorTicks[j], axisSize));
+ line.EndPoint = new Point(tickLength / 2.0, GetCoordinateFromTick(minorTicks[j], axisSize));
+ }
+ }
+ }
+ }
+
+ majorTicksPath.Data = majorTicksGeometry;
+ minorTicksPath.Data = minorTicksGeometry;
+
+ if (!drawMinorTicks)
+ drawMinorTicks = true;
+ }
+ }
+
+ ///
+ /// Positions child elements and determines a size for a Figure.
+ ///
+ /// The final area within the parent that Figure should use to arrange itself and its children.
+ /// The actual size used.
+ protected override Size ArrangeOverride(Size finalSize)
+ {
+ finalSize.Width = Math.Min(DesiredSize.Width, finalSize.Width);
+ finalSize.Height = Math.Min(DesiredSize.Height, finalSize.Height);
+
+ double labelArrangeOriginX = 0;
+ double labelArrangeOriginY = 0;
+
+
+ switch (AxisOrientation)
+ {
+ case AxisOrientation.Top:
+ majorTicksPath.Arrange(new Rect(0, finalSize.Height - tickLength, finalSize.Width, tickLength));
+ minorTicksPath.Arrange(new Rect(0, finalSize.Height - tickLength / 2.0, finalSize.Width, tickLength / 2.0));
+ break;
+ case AxisOrientation.Bottom:
+ majorTicksPath.Arrange(new Rect(0, 0, finalSize.Width, tickLength));
+ minorTicksPath.Arrange(new Rect(0, 0, finalSize.Width, tickLength / 2.0));
+ break;
+ case AxisOrientation.Right:
+ majorTicksPath.Arrange(new Rect(0, 0, tickLength, finalSize.Height));
+ minorTicksPath.Arrange(new Rect(0, 0, tickLength / 2.0, finalSize.Height));
+ break;
+ case AxisOrientation.Left:
+ majorTicksPath.Arrange(new Rect(Math.Max(0, finalSize.Width - tickLength), 0, tickLength, finalSize.Height));
+ minorTicksPath.Arrange(new Rect(Math.Max(0, finalSize.Width - tickLength / 2.0), 0, tickLength / 2.0, finalSize.Height));
+ labelArrangeOriginX = finalSize.Width - tickLength - CalculateMaxLabelWidth();
+ break;
+ }
+
+ foreach (var label in labels)
+ {
+ label.Arrange(new Rect(labelArrangeOriginX, labelArrangeOriginY, label.DesiredSize.Width, label.DesiredSize.Height));
+ }
+
+ return finalSize;
+ }
+
+ ///
+ /// Measures the size in layout required for child elements and determines a size for the Figure.
+ ///
+ /// The available size that this element can give to child elements. Infinity can be specified as a value to indicate that the element will size to whatever content is available.
+ /// The size that this element determines it needs during layout, based on its calculations of child element sizes.
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ if (Double.IsInfinity(availableSize.Width))
+ availableSize.Width = 128;
+ if (Double.IsInfinity(availableSize.Height))
+ availableSize.Height = 128;
+
+
+ Size effectiveSize = availableSize;
+
+
+ ClearLabels();
+ CreateTicks(effectiveSize);
+
+ double[] cTicks = Ticks.ToArray();
+ DrawCanvas(effectiveSize, cTicks);
+
+ foreach (UIElement child in Children)
+ {
+ child.Measure(availableSize);
+ }
+
+ double maxLabelWidth = CalculateMaxLabelWidth();
+ double maxLabelHeight = CalculateMaxLabelHeight();
+
+ switch (AxisOrientation)
+ {
+ case AxisOrientation.Top:
+
+ for (int i = 0; i < labels.Length; i++)
+ {
+ labels[i].RenderTransform = new TranslateTransform { X = GetCoordinateFromTick(cTicks[i], effectiveSize) - labels[i].DesiredSize.Width / 2, Y = 0 };
+ }
+ break;
+ case AxisOrientation.Bottom:
+ for (int i = 0; i < labels.Length; i++)
+ {
+ labels[i].RenderTransform = new TranslateTransform { X = GetCoordinateFromTick(cTicks[i], effectiveSize) - labels[i].DesiredSize.Width / 2, Y = majorTicksPath.DesiredSize.Height };
+ }
+ break;
+ case AxisOrientation.Right:
+ for (int i = 0; i < labels.Length; i++)
+ {
+ labels[i].RenderTransform = new TranslateTransform { X = majorTicksPath.DesiredSize.Width, Y = GetCoordinateFromTick(cTicks[i], effectiveSize) - labels[i].DesiredSize.Height / 2 };
+ }
+ break;
+ case AxisOrientation.Left:
+ for (int i = 0; i < labels.Length; i++)
+ {
+ labels[i].RenderTransform = new TranslateTransform { X = maxLabelWidth - labels[i].DesiredSize.Width, Y = GetCoordinateFromTick(cTicks[i], effectiveSize) - labels[i].DesiredSize.Height / 2 };
+ }
+ break;
+ }
+
+ if (IsHorizontal)
+ {
+ availableSize.Height = majorTicksPath.DesiredSize.Height + maxLabelHeight;
+ }
+ else
+ {
+ availableSize.Width = majorTicksPath.DesiredSize.Width + maxLabelWidth;
+ }
+
+ return availableSize;
+ }
+
+ private void ClearLabels()
+ {
+ if (labels != null)
+ {
+ foreach (var elt in labels)
+ {
+ if (Children.Contains(elt))
+ Children.Remove(elt);
+ }
+ }
+ }
+
+ private double CalculateMaxLabelHeight()
+ {
+ if (labels != null)
+ {
+ double max = 0;
+ for (int i = 0; i < labels.Length; i++)
+ if (Children.Contains(labels[i]) && labels[i].DesiredSize.Height > max)
+ max = labels[i].DesiredSize.Height;
+ return max;
+ }
+ return 0;
+ }
+
+ private double CalculateMaxLabelWidth()
+ {
+ if (labels != null)
+ {
+ double max = 0;
+ for (int i = 0; i < labels.Length; i++)
+ if (Children.Contains(labels[i]) && labels[i].DesiredSize.Width > max)
+ max = labels[i].DesiredSize.Width;
+ return max;
+ }
+ return 0;
+ }
+
+ private TickChange CheckLabelsArrangement(Size axisSize, IEnumerable inputLabels, double[] inputTicks)
+ {
+ var actualLabels1 = inputLabels.Select((label, i) => new { Label = label, Index = i });
+ var actualLabels2 = actualLabels1.Where(el => el.Label != null);
+ var actualLabels3 = actualLabels2.Select(el => new { Label = el.Label, Tick = inputTicks[el.Index] });
+ var actualLabels = actualLabels3.ToList();
+
+ actualLabels.ForEach(item => item.Label.Measure(axisSize));
+
+ var sizeInfos = actualLabels.Select(item =>
+ new { Offset = GetCoordinateFromTick(item.Tick, axisSize), Size = IsHorizontal ? item.Label.DesiredSize.Width : item.Label.DesiredSize.Height })
+ .OrderBy(item => item.Offset).ToArray();
+
+ // If distance between labels if smaller than threshold for any of the labels - decrease
+ for (int i = 0; i < sizeInfos.Length - 1; i++)
+ if ((sizeInfos[i].Offset + sizeInfos[i].Size * decreaseRatio / 2) > sizeInfos[i + 1].Offset)
+ return TickChange.Decrease;
+
+ // If distance between labels if larger than threshold for all of the labels - increase
+ TickChange res = TickChange.Increase;
+ for (int i = 0; i < sizeInfos.Length - 1; i++)
+ {
+ if ((sizeInfos[i].Offset + sizeInfos[i].Size * increaseRatio / 2) > sizeInfos[i + 1].Offset)
+ {
+ res = TickChange.OK;
+ break;
+ }
+ }
+
+ return res;
+ }
+
+ private double GetCoordinateFromTick(double tick, Size screenSize)
+ {
+ return ValueToScreen(DataTransform.DataToPlot(tick), screenSize, Range);
+ }
+
+ private double ValueToScreen(double value, Size screenSize, Range range)
+ {
+ double leftBound, rightBound, topBound, bottomBound;
+ leftBound = bottomBound = IsReversed ? range.Max : range.Min;
+ rightBound = topBound = IsReversed ? range.Min : range.Max;
+
+ if (IsHorizontal)
+ {
+ return range.IsPoint ?
+ (screenSize.Width / 2) :
+ ((value - leftBound) * screenSize.Width / (rightBound - leftBound));
+ }
+ else
+ {
+ return range.IsPoint ?
+ (screenSize.Height / 2) :
+ (screenSize.Height - (value - leftBound) * screenSize.Height / (rightBound - leftBound));
+ }
+ }
+
+ ///
+ /// Gets the value indcating whether the axis is horizontal or not.
+ ///
+ private bool IsHorizontal
+ {
+ get
+ {
+ return (AxisOrientation == AxisOrientation.Bottom || AxisOrientation == AxisOrientation.Top);
+ }
+ }
+ }
+
+ internal enum TickChange
+ {
+ Increase = -1,
+ OK = 0,
+ Decrease = 1
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Axes/AxisGrid.cs b/InteractiveDataDisplay_OneDll/src/Axes/AxisGrid.cs
new file mode 100644
index 000000000..3b83d13a0
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Axes/AxisGrid.cs
@@ -0,0 +1,239 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Linq;
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Shapes;
+using System.Windows.Data;
+using System.Collections.Generic;
+using System.ComponentModel;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Facilitates drawing of vertical and/or horizontal grid lines
+ ///
+ [Description("Grid of vertical and horizontal lines")]
+ public class AxisGrid : PlotBase
+ {
+ private Path path;
+
+ ///
+ /// Identifies the dependency property
+ ///
+ public static readonly DependencyProperty VerticalTicksProperty =
+ DependencyProperty.Register("VerticalTicks", typeof(IEnumerable), typeof(AxisGrid), new PropertyMetadata(new double[0],
+ (o, e) =>
+ {
+ AxisGrid axisGrid = (AxisGrid)o;
+ if (axisGrid != null)
+ {
+ axisGrid.InvalidateMeasure();
+ }
+ }));
+
+ ///
+ /// Identifies the dependency property
+ ///
+ public static readonly DependencyProperty HorizontalTicksProperty =
+ DependencyProperty.Register("HorizontalTicks", typeof(IEnumerable), typeof(AxisGrid), new PropertyMetadata(new double[0],
+ (o, e) =>
+ {
+ AxisGrid axisGrid = (AxisGrid)o;
+ if (axisGrid != null)
+ {
+ axisGrid.InvalidateMeasure();
+ }
+ }));
+
+ ///
+ /// Identifies the dependency property
+ ///
+ public static readonly DependencyProperty StrokeProperty =
+ DependencyProperty.Register("Stroke", typeof(SolidColorBrush), typeof(AxisGrid), new PropertyMetadata(new SolidColorBrush(Colors.LightGray),
+ (o, e) =>
+ {
+ AxisGrid axisGrid = (AxisGrid)o;
+ if (axisGrid != null)
+ {
+ axisGrid.InvalidateMeasure();
+ }
+ }));
+
+ ///
+ /// Gets or sets collection for horizontal lines coordinates
+ ///
+ [Category("InteractiveDataDisplay")]
+ [Description("Horizontal lines coordinates")]
+ public IEnumerable VerticalTicks
+ {
+ get { return (IEnumerable)GetValue(VerticalTicksProperty); }
+ set { SetValue(VerticalTicksProperty, value); }
+ }
+
+ ///
+ /// Gets or sets collection for vertical lines coordinates
+ ///
+ [Category("InteractiveDataDisplay")]
+ [Description("Vertical lines coordinates")]
+ public IEnumerable HorizontalTicks
+ {
+ get { return (IEnumerable)GetValue(HorizontalTicksProperty); }
+ set { SetValue(HorizontalTicksProperty, value); }
+ }
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty IsXAxisReversedProperty =
+ DependencyProperty.Register("IsXAxisReversed", typeof(bool), typeof(AxisGrid), new PropertyMetadata(false));
+
+ ///
+ /// Gets or sets a flag indicating whether the x-axis is reversed or not.
+ ///
+ [Category("InteractiveDataDisplay")]
+ public bool IsXAxisReversed
+ {
+ get { return (bool)GetValue(IsXAxisReversedProperty); }
+ set { SetValue(IsXAxisReversedProperty, value); }
+ }
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty IsYAxisReversedProperty =
+ DependencyProperty.Register("IsYAxisReversed", typeof(bool), typeof(AxisGrid), new PropertyMetadata(false));
+
+ ///
+ /// Gets or sets a flag indicating whether the y-axis is reversed or not.
+ ///
+ [Category("InteractiveDataDisplay")]
+ public bool IsYAxisReversed
+ {
+ get { return (bool)GetValue(IsYAxisReversedProperty); }
+ set { SetValue(IsYAxisReversedProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the Brush that specifies how the horizontal and vertical lines is painted
+ ///
+ [Category("Appearance")]
+ public SolidColorBrush Stroke
+ {
+ get { return (SolidColorBrush)GetValue(StrokeProperty); }
+ set { SetValue(StrokeProperty, value); }
+ }
+
+ ///
+ /// Initializes new instance of class
+ ///
+ public AxisGrid()
+ {
+ HorizontalTicks = new double[0];
+ VerticalTicks = new double[0];
+
+ path = new Path();
+ BindingOperations.SetBinding(path, Path.StrokeProperty, new Binding("Stroke") { Source = this, Mode = BindingMode.TwoWay });
+ path.StrokeThickness = 1.0;
+ Children.Add(path);
+ }
+
+ ///
+ /// Measures the size in layout required for child elements and determines a size for the AxisGrid.
+ ///
+ /// The available size that this element can give to child elements. Infinity can be specified as a value to indicate that the element will size to whatever content is available.
+ /// The size that this element determines it needs during layout, based on its calculations of child element sizes.
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ if (Double.IsInfinity(availableSize.Width))
+ availableSize.Width = 1024;
+ if (Double.IsInfinity(availableSize.Height))
+ availableSize.Height = 1024;
+
+
+ GeometryGroup group = new GeometryGroup();
+
+ double[] hTicks = HorizontalTicks.ToArray();
+ double[] vTicks = VerticalTicks.ToArray();
+
+ if (hTicks != null && hTicks.Length > 0)
+ {
+ double minY = 0;
+ double maxY = availableSize.Height;
+
+ int i = 0;
+ if (hTicks[0] < ActualPlotRect.X.Min) i++;
+ for (; i < hTicks.Length; i++)
+ {
+ double screenX = GetHorizontalCoordinateFromTick(hTicks[i], availableSize.Width, ActualPlotRect.X);
+ LineGeometry line = new LineGeometry();
+ line.StartPoint = new Point(screenX, minY);
+ line.EndPoint = new Point(screenX, maxY);
+ group.Children.Add(line);
+ }
+ }
+
+ if (vTicks != null && vTicks.Length > 0)
+ {
+ double minX = 0;
+ double maxX = availableSize.Width;
+
+ int i = 0;
+ if (vTicks[0] < ActualPlotRect.Y.Min) i++;
+ for (; i < vTicks.Length; i++)
+ {
+ double screenY = GetVerticalCoordinateFromTick(vTicks[i], availableSize.Height, ActualPlotRect.Y);
+ LineGeometry line = new LineGeometry();
+ line.StartPoint = new Point(minX, screenY);
+ line.EndPoint = new Point(maxX, screenY);
+ group.Children.Add(line);
+ }
+ }
+
+ path.Data = group;
+
+ foreach (UIElement child in Children)
+ {
+ child.Measure(availableSize);
+ }
+
+ return availableSize;
+ }
+
+ ///
+ /// Positions child elements and determines a size for a AxisGrid
+ ///
+ /// The final area within the parent that AxisGrid should use to arrange itself and its children
+ /// The actual size used
+ protected override Size ArrangeOverride(Size finalSize)
+ {
+ foreach (UIElement child in Children)
+ {
+ child.Arrange(new Rect(new Point(0, 0), finalSize));
+ }
+
+ return finalSize;
+ }
+
+ private double GetHorizontalCoordinateFromTick(double tick, double screenSize, Range range)
+ {
+ return ValueToScreen(XDataTransform.DataToPlot(tick), screenSize, range, IsXAxisReversed);
+ }
+
+ private double GetVerticalCoordinateFromTick(double tick, double screenSize, Range range)
+ {
+ return screenSize - ValueToScreen(YDataTransform.DataToPlot(tick), screenSize, range, IsYAxisReversed);
+ }
+
+
+ private static double ValueToScreen(double value, double dimensionSize, Range range, bool isReversed)
+ {
+ var bound1 = isReversed ? range.Max : range.Min;
+ var bound2 = isReversed ? range.Min : range.Max;
+ return (value - bound1) * dimensionSize / (bound2 - bound1);
+ }
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Axes/AxisOrientation.cs b/InteractiveDataDisplay_OneDll/src/Axes/AxisOrientation.cs
new file mode 100644
index 000000000..78122ab58
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Axes/AxisOrientation.cs
@@ -0,0 +1,29 @@
+// Copyright © Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Specifies axes orientation.
+ ///
+ public enum AxisOrientation
+ {
+ ///
+ /// Axis will be vertical and ticks will be aligned to right.
+ ///
+ Left,
+ ///
+ /// Axis will be vertical and ticks will be aligned to left.
+ ///
+ Right,
+ ///
+ /// Axis will be horizontal and ticks will be aligned to bottom.
+ ///
+ Top,
+ ///
+ /// Axis will be horizontal and ticks will be aligned to top.
+ ///
+ Bottom
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Axes/ILabelProvider.cs b/InteractiveDataDisplay_OneDll/src/Axes/ILabelProvider.cs
new file mode 100644
index 000000000..319a43b83
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Axes/ILabelProvider.cs
@@ -0,0 +1,21 @@
+// Copyright © Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System.Windows;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Defines a method to create labels as an array of from an array of double values.
+ ///
+ public interface ILabelProvider
+ {
+ ///
+ /// Generates an array of labels from an array of double.
+ ///
+ /// An array of double ticks.
+ ///
+ FrameworkElement[] GetLabels(double[] ticks);
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Axes/LabelProvider.cs b/InteractiveDataDisplay_OneDll/src/Axes/LabelProvider.cs
new file mode 100644
index 000000000..2ac3880f6
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Axes/LabelProvider.cs
@@ -0,0 +1,38 @@
+// Copyright © Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Collections.Generic;
+using System.Globalization;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Provides mechanisms to generate labels displayed on an axis by double ticks.
+ ///
+ public class LabelProvider : ILabelProvider
+ {
+ ///
+ /// Generates an array of labels from an array of double.
+ ///
+ /// An array of double ticks.
+ /// An array of .
+ public FrameworkElement[] GetLabels(double[] ticks)
+ {
+ if (ticks == null)
+ throw new ArgumentNullException("ticks");
+
+ List Labels = new List();
+ foreach (double tick in ticks)
+ {
+ TextBlock text = new TextBlock();
+ text.Text = tick.ToString(CultureInfo.InvariantCulture);
+ Labels.Add(text);
+ }
+ return Labels.ToArray();
+ }
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Axes/MinorTicksProvider.cs b/InteractiveDataDisplay_OneDll/src/Axes/MinorTicksProvider.cs
new file mode 100644
index 000000000..158983d02
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Axes/MinorTicksProvider.cs
@@ -0,0 +1,92 @@
+// Copyright © Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Provides mechanisms to generate minor ticks displayed on an axis.
+ ///
+ public class MinorTicksProvider
+ {
+ ///
+ /// Default ticks count for provider.
+ ///
+ public static readonly int DefaultTicksCount = 3;
+ private int ticksCount;
+
+ ///
+ /// Initializes a new instance of class with default ticks count.
+ ///
+ public MinorTicksProvider()
+ {
+ ticksCount = DefaultTicksCount;
+ }
+
+ ///
+ /// Gets or sets the count of generated ticks.
+ ///
+ public int TicksCount
+ {
+ get { return ticksCount; }
+ set { ticksCount = value; }
+ }
+
+ ///
+ /// Generates minor ticks for given array of major ticks and range.
+ ///
+ /// The range.
+ /// An array of major ticks.
+ ///
+ public double[] CreateTicks(Range range, double[] ticks)
+ {
+ if (ticks == null)
+ throw new ArgumentNullException("ticks");
+
+ if (ticks.Count() < 2)
+ return null;
+
+ List res = new List();
+ double step = (ticks[1] - ticks[0]) / (ticksCount + 1);
+ int i = 1;
+ if (range.Min > ticks[0])
+ {
+ double x0 = ticks[1] - i * step;
+ res.Add(x0);
+ while (res[res.Count - 1] >= range.Min)
+ {
+ res.Add(x0 - i * step);
+ i++;
+ }
+ i++;
+ }
+
+ int fin = ticks.Length - 1;
+ if (ticks[fin] > range.Max)
+ fin--;
+ for (i = 0; i < fin; i++)
+ {
+ double x0 = ticks[i] + step;
+ for (int j = 0; j < ticksCount; j++)
+ res.Add(x0 + j * step);
+ }
+
+ if (fin != ticks.Length - 1)
+ {
+ double x0 = ticks[fin] + step;
+ res.Add(x0);
+ i = 1;
+ while (res[res.Count - 1] <= range.Max)
+ {
+ res.Add(x0 + i * step);
+ i++;
+ }
+ }
+ return res.ToArray();
+ }
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Axes/PlotAxis.cs b/InteractiveDataDisplay_OneDll/src/Axes/PlotAxis.cs
new file mode 100644
index 000000000..690f4f980
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Axes/PlotAxis.cs
@@ -0,0 +1,193 @@
+// Copyright © Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Windows.Data;
+
+namespace InteractiveDataDisplay.WPF
+{
+ /// Facilitates drawing of vertical or horizontal coordinate axis connected to parent element
+ [TemplatePart(Name = "PART_Axis", Type = typeof(Axis))]
+ [Description("Figure's axis")]
+ public sealed class PlotAxis : ContentControl
+ {
+ private PlotBase masterPlot = null;
+ private Axis axis;
+
+ ///
+ /// Initializes new instance of class
+ ///
+ public PlotAxis()
+ {
+ XDataTransform = new IdentityDataTransform();
+ YDataTransform = new IdentityDataTransform();
+ Ticks = new double[0];
+ DefaultStyleKey = typeof(PlotAxis);
+ Loaded += PlotAxisLoaded;
+ Unloaded += PlotAxisUnloaded;
+ }
+
+ private void PlotAxisUnloaded(object sender, RoutedEventArgs e)
+ {
+ masterPlot = null;
+ }
+
+ private void PlotAxisLoaded(object sender, RoutedEventArgs e)
+ {
+ masterPlot = PlotBase.FindMaster(this);
+ if (masterPlot != null)
+ InvalidateAxis();
+ }
+
+ ///
+ /// Measures the size in layout required for child elements and determines a size for parent.
+ ///
+ /// The available size that this element can give to child elements. Infinity can be specified as a value to indicate that the element will size to whatever content is available.
+ /// The size that this element determines it needs during layout, based on its calculations of child element sizes.
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ InvalidateAxis();
+ return base.MeasureOverride(availableSize);
+ }
+
+ private void InvalidateAxis()
+ {
+ if (axis != null)
+ {
+ if (axis.AxisOrientation == AxisOrientation.Left ||
+ axis.AxisOrientation == AxisOrientation.Right)
+ {
+ if (masterPlot != null)
+ {
+ axis.Range = new Range(masterPlot.ActualPlotRect.YMin, masterPlot.ActualPlotRect.YMax);
+ }
+ axis.DataTransform = YDataTransform;
+ }
+ else
+ {
+ if (masterPlot != null)
+ {
+ axis.Range = new Range(masterPlot.ActualPlotRect.XMin, masterPlot.ActualPlotRect.XMax);
+ }
+ axis.DataTransform = XDataTransform;
+ }
+ }
+ }
+
+ ///
+ /// Invoked whenever application code or internal processes call ApplyTemplate
+ ///
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ axis = GetTemplateChild("PART_Axis") as Axis;
+ if (axis == null)
+ {
+ throw new InvalidOperationException("Invalid template!");
+ }
+ BindingOperations.SetBinding(this.axis, Axis.TicksProperty, new Binding("Ticks")
+ {
+ Source = this,
+ Mode = BindingMode.TwoWay
+ });
+ InvalidateAxis();
+ }
+
+ ///
+ /// Gets or sets a set of double values displayed as axis ticks.
+ ///
+ [Browsable(false)]
+ public IEnumerable Ticks
+ {
+ get { return (IEnumerable)GetValue(TicksProperty); }
+ set { SetValue(TicksProperty, value); }
+ }
+
+ /// Identify property
+ public static readonly DependencyProperty TicksProperty =
+ DependencyProperty.Register("Ticks", typeof(IEnumerable), typeof(PlotAxis), new PropertyMetadata(new double[0]));
+
+ ///
+ /// Gets or Sets orientation of axis and location of labels
+ ///
+ [Category("InteractiveDataDisplay")]
+ [Description("Defines orientation of axis and location of labels")]
+ public AxisOrientation AxisOrientation
+ {
+ get { return (AxisOrientation)GetValue(AxisOrientationProperty); }
+ set { SetValue(AxisOrientationProperty, value); }
+ }
+
+ /// Identify property
+ public static readonly DependencyProperty AxisOrientationProperty =
+ DependencyProperty.Register("AxisOrientation", typeof(AxisOrientation), typeof(PlotAxis), new PropertyMetadata(AxisOrientation.Bottom));
+
+ ///
+ /// Gets or sets a flag indicating whether the axis is reversed or not.
+ ///
+ /// Axis is not reversed by default.
+ [Category("InteractiveDataDisplay")]
+ [Description("Defines orientation of axis and location of labels")]
+ public bool IsReversed
+ {
+ get { return (bool)GetValue(IsReversedProperty); }
+ set { SetValue(IsReversedProperty, value); }
+ }
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty IsReversedProperty =
+ DependencyProperty.Register("IsReversed", typeof(bool), typeof(PlotAxis), new PropertyMetadata(false));
+
+ /// Gets or sets transform from user data to horizontal plot coordinate.
+ /// By default transform is
+ ///
+ [Category("InteractiveDataDisplay")]
+ public DataTransform XDataTransform
+ {
+ get { return (DataTransform)GetValue(XDataTransformProperty); }
+ set { SetValue(XDataTransformProperty, value); }
+ }
+
+ /// Gets or sets transform from user data to vertical plot coordinate.
+ /// By default transform is
+ ///
+ [Category("InteractiveDataDisplay")]
+ public DataTransform YDataTransform
+ {
+ get { return (DataTransform)GetValue(YDataTransformProperty); }
+ set { SetValue(YDataTransformProperty, value); }
+ }
+
+ /// Identify property
+ public static readonly DependencyProperty XDataTransformProperty =
+ DependencyProperty.Register("XDataTransform", typeof(DataTransform), typeof(PlotAxis), new PropertyMetadata(null,
+ (o, e) =>
+ {
+ PlotAxis plot = o as PlotAxis;
+ if (plot != null)
+ {
+ plot.InvalidateAxis();
+ }
+ }));
+
+ /// Identify property
+ public static readonly DependencyProperty YDataTransformProperty =
+ DependencyProperty.Register("YDataTransform", typeof(DataTransform), typeof(PlotAxis), new PropertyMetadata(null,
+ (o, e) =>
+ {
+ PlotAxis plot = o as PlotAxis;
+ if (plot != null)
+ {
+ plot.InvalidateAxis();
+ }
+ }));
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Axes/RoundHelper.cs b/InteractiveDataDisplay_OneDll/src/Axes/RoundHelper.cs
new file mode 100644
index 000000000..710446a55
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Axes/RoundHelper.cs
@@ -0,0 +1,71 @@
+// Copyright © Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+namespace InteractiveDataDisplay.WPF
+{
+ internal static class RoundHelper
+ {
+ //internal static int GetDifferenceLog(double min, double max)
+ //{
+ // return (int)Math.Log(Math.Abs(max - min));
+ //}
+
+ internal static double Floor(double number, int rem)
+ {
+ if (rem <= 0)
+ {
+ rem = MathHelper.Clamp(-rem, 0, 15);
+ }
+ double pow = Math.Pow(10, rem - 1);
+ double val = pow * Math.Floor((double)(number / Math.Pow(10, rem - 1)));
+ return val;
+
+ }
+
+ internal static double Round(double number, int rem)
+ {
+ if (rem <= 0)
+ {
+ rem = MathHelper.Clamp(-rem, 0, 15);
+ return Math.Round(number, rem);
+ }
+ else
+ {
+ double pow = Math.Pow(10, rem - 1);
+ double val = pow * Math.Round(number / Math.Pow(10, rem - 1));
+ return val;
+ }
+ }
+
+ //internal static RoundingInfo CreateRoundedRange(double min, double max)
+ //{
+ // double delta = max - min;
+
+ // if (delta == 0)
+ // return new RoundingInfo { Min = min, Max = max, Log = 0 };
+
+ // int log = (int)Math.Round(Math.Log10(Math.Abs(delta))) + 1;
+
+ // double newMin = Round(min, log);
+ // double newMax = Round(max, log);
+ // if (newMin == newMax)
+ // {
+ // log--;
+ // newMin = Round(min, log);
+ // newMax = Round(max, log);
+ // }
+
+ // return new RoundingInfo { Min = newMin, Max = newMax, Log = log };
+ //}
+ }
+
+ //[DebuggerDisplay("{Min} - {Max}, Log = {Log}")]
+ //internal sealed class RoundingInfo
+ //{
+ // public double Min { get; set; }
+ // public double Max { get; set; }
+ // public int Log { get; set; }
+ //}
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Axes/TicksProvider.cs b/InteractiveDataDisplay_OneDll/src/Axes/TicksProvider.cs
new file mode 100644
index 000000000..e03f0f39d
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Axes/TicksProvider.cs
@@ -0,0 +1,132 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Provides mechanisms to generate ticks displayed on an axis.
+ ///
+ public class TicksProvider
+ {
+ private int delta = 1;
+ private int beta = 0;
+
+ ///
+ /// Initializes a new instance of class with default .
+ ///
+ public TicksProvider()
+ {
+ minorProvider = new MinorTicksProvider();
+ }
+
+ private readonly MinorTicksProvider minorProvider;
+ ///
+ /// Gets the .
+ ///
+ public MinorTicksProvider MinorProvider
+ {
+ get { return minorProvider; }
+ }
+
+ private Range range = new Range(0, 1);
+ ///
+ /// Gets or sets the range of axis.
+ ///
+ public Range Range
+ {
+ get { return range; }
+ set
+ {
+ range = value;
+ delta = 1;
+ beta = (int)Math.Round(Math.Log10(range.Max - range.Min)) - 1;
+ }
+ }
+
+ ///
+ /// Gets an array of ticks for specified axis range.
+ ///
+ public double[] GetTicks()
+ {
+ double start = Range.Min;
+ double finish = Range.Max;
+ double d = finish - start;
+
+ if (d == 0)
+ return new double[] { start, finish };
+
+ double temp = delta * Math.Pow(10, beta);
+ double min = Math.Floor(start / temp);
+ double max = Math.Floor(finish / temp);
+ int count = (int)(max - min + 1);
+ List res = new List();
+ double x0 = min * temp;
+ for (int i = 0; i < count + 1; i++)
+ {
+ double v = RoundHelper.Round(x0 + i * temp, beta);
+ if(v >= start && v <= finish)
+ res.Add(v);
+ }
+ return res.ToArray();
+ }
+
+ ///
+ /// Decreases the tick count.
+ ///
+ public void DecreaseTickCount()
+ {
+ if (delta == 1)
+ {
+ delta = 2;
+ }
+ else if (delta == 2)
+ {
+ delta = 5;
+ }
+ else if (delta == 5)
+ {
+ delta = 1;
+ beta++;
+ }
+ }
+
+ ///
+ /// Increases the tick count.
+ ///
+ public void IncreaseTickCount()
+ {
+ if (delta == 1)
+ {
+ delta = 5;
+ beta--;
+ }
+ else if (delta == 2)
+ {
+ delta = 1;
+ }
+ else if (delta == 5)
+ {
+ delta = 2;
+ }
+ }
+
+ ///
+ /// Generates minor ticks in specified range.
+ ///
+ /// The range.
+ /// An array of minor ticks.
+ public double[] GetMinorTicks(Range range)
+ {
+ var ticks = new List(GetTicks());
+ double temp = delta * Math.Pow(10, beta);
+ ticks.Insert(0, RoundHelper.Round(ticks[0] - temp, beta));
+ ticks.Add(RoundHelper.Round(ticks[ticks.Count - 1] + temp, beta));
+ return minorProvider.CreateTicks(range, ticks.ToArray());
+ }
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Chart.cs b/InteractiveDataDisplay_OneDll/src/Chart.cs
new file mode 100644
index 000000000..014121c60
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Chart.cs
@@ -0,0 +1,332 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Markup;
+using System.ComponentModel;
+using System.Windows.Data;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Ready to use figure with left and bottom axes, grid, legend, mouse and keyboard navigation
+ ///
+ [ContentProperty("Content")]
+ [Description("Ready to use figure")]
+ public class Chart : ContentControl
+ {
+ ///
+ /// Initializes a new instance of class.
+ ///
+ public Chart()
+ {
+ DefaultStyleKey = typeof(Chart);
+ Background = new SolidColorBrush(Colors.White);
+ Foreground = new SolidColorBrush(Colors.Black);
+ LegendContent = new LegendItemsPanel();
+ }
+
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+ PlotAxis plotAxis = base.GetTemplateChild("PART_horizontalAxis") as PlotAxis;
+ PlotAxis plotAxis2 = base.GetTemplateChild("PART_verticalAxis") as PlotAxis;
+ AxisGrid axisGrid = base.GetTemplateChild("PART_axisGrid") as AxisGrid;
+ if (plotAxis == null || plotAxis2 == null || axisGrid == null)
+ {
+ return;
+ }
+ BindingOperations.SetBinding(axisGrid, AxisGrid.HorizontalTicksProperty, new Binding("Ticks")
+ {
+ Source = plotAxis
+ });
+ BindingOperations.SetBinding(axisGrid, AxisGrid.VerticalTicksProperty, new Binding("Ticks")
+ {
+ Source = plotAxis2
+ });
+ }
+ ///
+ /// Raises the GotFocus event.
+ ///
+ /// An EventArgs that contains the event data.
+ protected override void OnGotFocus(RoutedEventArgs e)
+ {
+ VisualStateManager.GoToState(this, "Focused", false);
+ base.OnGotFocus(e);
+ }
+
+ ///
+ /// Raises the LostFocus event.
+ ///
+ /// An EventArgs that contains the event data.
+ protected override void OnLostFocus(RoutedEventArgs e)
+ {
+ VisualStateManager.GoToState(this, "Unfocused", false);
+ base.OnLostFocus(e);
+ }
+
+ ///
+ /// Gets or sets legend content
+ ///
+ [Category("InteractiveDataDisplay")]
+ public object LegendContent
+ {
+ get { return (object)GetValue(LegendContentProperty); }
+ set { SetValue(LegendContentProperty, value); }
+ }
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty LegendContentProperty =
+ DependencyProperty.Register("LegendContent", typeof(object), typeof(Chart), new PropertyMetadata(null));
+
+ ///
+ /// Gets or sets visibility of LegendControl
+ ///
+ [Category("InteractiveDataDisplay")]
+ public Visibility LegendVisibility
+ {
+ get { return (Visibility)GetValue(LegendVisibilityProperty); }
+ set { SetValue(LegendVisibilityProperty, value); }
+ }
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty LegendVisibilityProperty =
+ DependencyProperty.Register("LegendVisibility", typeof(Visibility), typeof(Chart), new PropertyMetadata(Visibility.Visible));
+
+ ///
+ /// Gets or sets auto fit mode. property of all
+ /// plots in compositions are updated instantly to have same value.
+ ///
+ [Category("InteractiveDataDisplay")]
+ public bool IsAutoFitEnabled
+ {
+ get { return (bool)GetValue(IsAutoFitEnabledProperty); }
+ set { SetValue(IsAutoFitEnabledProperty, value); }
+ }
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty IsAutoFitEnabledProperty =
+ DependencyProperty.Register("IsAutoFitEnabled", typeof(bool), typeof(Chart), new PropertyMetadata(true));
+
+ ///
+ /// Gets or sets desired plot width in plot coordinates. Settings this property
+ /// turns off auto fit mode.
+ ///
+ [Category("InteractiveDataDisplay")]
+ public double PlotWidth
+ {
+ get { return (double)GetValue(PlotWidthProperty); }
+ set { SetValue(PlotWidthProperty, value); }
+ }
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty PlotWidthProperty =
+ DependencyProperty.Register("PlotWidth", typeof(double), typeof(Chart), new PropertyMetadata(1.0));
+
+ ///
+ /// Gets or sets desired plot height in plot coordinates. Settings this property
+ /// turns off auto fit mode.
+ ///
+ [Category("InteractiveDataDisplay")]
+ public double PlotHeight
+ {
+ get { return (double)GetValue(PlotHeightProperty); }
+ set { SetValue(PlotHeightProperty, value); }
+ }
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty PlotHeightProperty =
+ DependencyProperty.Register("PlotHeight", typeof(double), typeof(Chart), new PropertyMetadata(1.0));
+
+ ///
+ /// Gets or sets desired minimal visible horizontal coordinate in plot coordinates. Settings this property
+ /// turns off auto fit mode.
+ ///
+ [Category("InteractiveDataDisplay")]
+ public double PlotOriginX
+ {
+ get { return (double)GetValue(PlotOriginXProperty); }
+ set { SetValue(PlotOriginXProperty, value); }
+ }
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty PlotOriginXProperty =
+ DependencyProperty.Register("PlotOriginX", typeof(double), typeof(Chart), new PropertyMetadata(0.0));
+
+ ///
+ /// Gets or sets desired minimal visible vertical coordinate in plot coordinates. Settings this property
+ /// turns off auto fit mode.
+ ///
+ [Category("InteractiveDataDisplay")]
+ public double PlotOriginY
+ {
+ get { return (double)GetValue(PlotOriginYProperty); }
+ set { SetValue(PlotOriginYProperty, value); }
+ }
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty PlotOriginYProperty =
+ DependencyProperty.Register("PlotOriginY", typeof(double), typeof(Chart), new PropertyMetadata(0.0));
+
+ ///
+ /// Desired ratio of horizontal scale to vertical scale. Values less than or equal to zero
+ /// represent unspecified aspect ratio.
+ ///
+ [Category("InteractiveDataDisplay")]
+ public double AspectRatio
+ {
+ get { return (double)GetValue(AspectRatioProperty); }
+ set { SetValue(AspectRatioProperty, value); }
+ }
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty AspectRatioProperty =
+ DependencyProperty.Register("AspectRatio", typeof(double), typeof(Chart), new PropertyMetadata(0.0));
+
+ /// Gets or sets chart title. Chart title is an object that is shown centered above plot area.
+ /// Chart title is used inside and can be a
+ /// .
+ [Category("InteractiveDataDisplay")]
+ public object Title
+ {
+ get { return (object)GetValue(TitleProperty); }
+ set { SetValue(TitleProperty, value); }
+ }
+
+ /// Identifies dependency property
+ public static readonly DependencyProperty TitleProperty =
+ DependencyProperty.Register("Title", typeof(object), typeof(Chart), new PropertyMetadata(null));
+
+ /// Gets or sets bottom axis title. Bottom axis title is an object that is centered under plot area.
+ /// Bottom axis title is used inside and can be a
+ /// .
+ [Category("InteractiveDataDisplay")]
+ public object BottomTitle
+ {
+ get { return (object)GetValue(BottomTitleProperty); }
+ set { SetValue(BottomTitleProperty, value); }
+ }
+
+ /// Identifies dependency property
+ public static readonly DependencyProperty BottomTitleProperty =
+ DependencyProperty.Register("BottomTitle", typeof(object), typeof(Chart), new PropertyMetadata(null));
+
+ /// Gets or sets right axis title. Right axis title is an object that is vertically centered
+ /// and located to the right from plot area.
+ /// Right axis title is used inside and can be a
+ /// .
+ [Category("InteractiveDataDisplay")]
+ public object RightTitle
+ {
+ get { return (object)GetValue(RightTitleProperty); }
+ set { SetValue(RightTitleProperty, value); }
+ }
+
+ /// Identifies dependency property
+ public static readonly DependencyProperty RightTitleProperty =
+ DependencyProperty.Register("RightTitle", typeof(object), typeof(Chart), new PropertyMetadata(null));
+
+ /// Gets or sets left axis title. Left axis title is an object that is vertically centered
+ /// and located to the left from plot area.
+ /// Left axis title is used inside and can be a
+ /// .
+ [Category("InteractiveDataDisplay")]
+ public object LeftTitle
+ {
+ get { return (object)GetValue(LeftTitleProperty); }
+ set { SetValue(LeftTitleProperty, value); }
+ }
+
+ /// Identifies dependency property
+ public static readonly DependencyProperty LeftTitleProperty =
+ DependencyProperty.Register("LeftTitle", typeof(object), typeof(Chart), new PropertyMetadata(null));
+
+ /// Gets or sets thickness of border surrounding Chart center. This value also is taken into
+ /// account when computing screen padding
+ [Category("InteractiveDataDisplay")]
+ public new Thickness BorderThickness
+ {
+ get { return (Thickness)GetValue(BorderThicknessProperty); }
+ set { SetValue(BorderThicknessProperty, value); }
+ }
+
+ /// Identifies property
+ public static new readonly DependencyProperty BorderThicknessProperty =
+ DependencyProperty.Register("BorderThickness", typeof(Thickness), typeof(Chart), new PropertyMetadata(new Thickness(1)));
+
+ /// Gets or sets vertical navigation status. True means that user can navigate along Y axis
+ [Category("InteractiveDataDisplay")]
+ public bool IsVerticalNavigationEnabled
+ {
+ get { return (bool)GetValue(IsVerticalNavigationEnabledProperty); }
+ set { SetValue(IsVerticalNavigationEnabledProperty, value); }
+ }
+
+ /// Identifies property
+ public static readonly DependencyProperty IsVerticalNavigationEnabledProperty =
+ DependencyProperty.Register("IsVerticalNavigationEnabled", typeof(bool), typeof(Chart), new PropertyMetadata(true));
+
+ /// Gets or sets horizontal navigation status. True means that user can navigate along X axis
+ [Category("InteractiveDataDisplay")]
+ public bool IsHorizontalNavigationEnabled
+ {
+ get { return (bool)GetValue(IsHorizontalNavigationEnabledProperty); }
+ set { SetValue(IsHorizontalNavigationEnabledProperty, value); }
+ }
+
+ /// Identifies property
+ public static readonly DependencyProperty IsHorizontalNavigationEnabledProperty =
+ DependencyProperty.Register("IsHorizontalNavigationEnabled", typeof(bool), typeof(Chart), new PropertyMetadata(true));
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty IsXAxisReversedProperty =
+ DependencyProperty.Register("IsXAxisReversed", typeof(bool), typeof(Chart), new PropertyMetadata(false));
+
+ ///
+ /// Gets or sets a flag indicating whether the x-axis is reversed or not.
+ ///
+ [Category("InteractiveDataDisplay")]
+ public bool IsXAxisReversed
+ {
+ get { return (bool)GetValue(IsXAxisReversedProperty); }
+ set { SetValue(IsXAxisReversedProperty, value); }
+ }
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty IsYAxisReversedProperty =
+ DependencyProperty.Register("IsYAxisReversed", typeof(bool), typeof(Chart), new PropertyMetadata(false));
+
+ ///
+ /// Gets or sets a flag indicating whether the y-axis is reversed or not.
+ ///
+ [Category("InteractiveDataDisplay")]
+ public bool IsYAxisReversed
+ {
+ get { return (bool)GetValue(IsYAxisReversedProperty); }
+ set { SetValue(IsYAxisReversedProperty, value); }
+ }
+
+ }
+}
diff --git a/InteractiveDataDisplay_OneDll/src/Common/ArrayHelper.cs b/InteractiveDataDisplay_OneDll/src/Common/ArrayHelper.cs
new file mode 100644
index 000000000..7f0c873a2
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Common/ArrayHelper.cs
@@ -0,0 +1,176 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Helper class for performing manipulations with arrays
+ ///
+ public static class ArrayExtensions
+ {
+ /// Gets index of item that is nearest to .
+ /// Array in ascending order.
+ /// Value to look for.
+ /// Index of closest element or -1 if
+ /// is less than first element or greater than last.
+ public static int GetNearestIndex(double[] array, double value)
+ {
+ if (array == null)
+ throw new ArgumentNullException("array");
+ int i = Array.BinarySearch(array, value);
+ if (i >= 0)
+ return i;
+ i = ~i;
+ if (i == array.Length)
+ return -1; // greater than last value
+ if (i == 0)
+ return -1; // less than first value
+ return (value - array[i - 1] > array[i] - value) ? (i) : (i - 1);
+ }
+
+ #region 1D array conversion utils
+
+ /// Converts array of floats to array of doubles
+ /// Array of float
+ /// Array of same length but with double elements
+ public static double[] ToDoubleArray(this float[] array)
+ {
+ if (array == null)
+ throw new ArgumentNullException("array");
+ double[] result = new double[array.Length];
+ for (int i = 0; i < array.Length; i++)
+ result[i] = array[i];
+ return result;
+ }
+
+ /// Converts array of 32-bit integers to array of doubles
+ /// Array of 32-bit integers
+ /// Array of same length but with double elements
+ public static double[] ToDoubleArray(this int[] array)
+ {
+ if (array == null)
+ throw new ArgumentNullException("array");
+ double[] result = new double[array.Length];
+ for (int i = 0; i < array.Length; i++)
+ result[i] = array[i];
+ return result;
+ }
+
+ /// Converts array of booleans to array of doubles where true maps to 1, false maps to 0
+ /// Array of booleans
+ /// Array of same dimensions but with double elements
+ public static double[] ToDoubleArray(this bool[] array)
+ {
+ if (array == null)
+ throw new ArgumentNullException("array");
+ double[] result = new double[array.Length];
+ for (int i = 0; i < array.Length; i++)
+ result[i] = array[i] ? 1 : 0 ;
+ return result;
+ }
+
+ /// Converts array of doubles, floats, integers or booleans to array of doubles where true maps to 1, false maps to 0
+ /// Source array
+ /// Array of same length but with double elements
+ public static double[] ToDoubleArray(Array array)
+ {
+ if (array == null)
+ throw new ArgumentNullException("array");
+ if (array.Rank != 1)
+ throw new InvalidOperationException("Source array is not 1D");
+ var elemType = array.GetType().GetElementType();
+ if (elemType == typeof(double))
+ return (double[])array;
+ if (elemType == typeof(float))
+ return ArrayExtensions.ToDoubleArray((float[])array);
+ else if (elemType == typeof(int))
+ return ArrayExtensions.ToDoubleArray((int[])array);
+ else if (elemType == typeof(bool))
+ return ArrayExtensions.ToDoubleArray((bool[])array);
+ else
+ throw new NotSupportedException("Conversions of 1D arrays of " + elemType.Name + " to double[] is not supported");
+ }
+
+ #endregion
+
+ #region 2D array conversion utils
+
+ /// Converts 2D array of floats to 2D array of doubles
+ /// 2D array of float
+ /// 2D array of same dimensions but with double elements
+ [CLSCompliantAttribute(false)]
+ public static double[,] ToDoubleArray(this float[,] array)
+ {
+ if (array == null)
+ throw new ArgumentNullException("array");
+ int n = array.GetLength(0);
+ int m = array.GetLength(1);
+ double[,] result = new double[n, m];
+ for (int i = 0; i < n; i++)
+ for (int j = 0; j < m; j++)
+ result[i, j] = array[i, j];
+ return result;
+ }
+
+ /// Converts 2D array of 32-bit integers to 2D array of doubles
+ /// 2D array of 32-bit integers
+ /// 2D array of same dimensions but with double elements
+ [CLSCompliantAttribute(false)]
+ public static double[,] ToDoubleArray(this int[,] array)
+ {
+ if (array == null)
+ throw new ArgumentNullException("array");
+ int n = array.GetLength(0);
+ int m = array.GetLength(1);
+ double[,] result = new double[n, m];
+ for (int i = 0; i < n; i++)
+ for (int j = 0; j < m; j++)
+ result[i, j] = array[i, j];
+ return result;
+ }
+
+ /// Converts 2D array of booleans to 2D array of doubles where true maps to 1, false maps to 0
+ /// 2D array of booleans
+ /// 2D array of same dimensions but with double elements
+ [CLSCompliantAttribute(false)]
+ public static double[,] ToDoubleArray(this bool[,] array)
+ {
+ if (array == null)
+ throw new ArgumentNullException("array");
+ int n = array.GetLength(0);
+ int m = array.GetLength(1);
+ double[,] result = new double[n, m];
+ for (int i = 0; i < n; i++)
+ for (int j = 0; j < m; j++)
+ result[i, j] = array[i, j] ? 1 : 0;
+ return result;
+ }
+
+ /// Converts 2D array of doubles, floats, integers or booleans to 2D array of doubles where true maps to 1, false maps to 0
+ /// 2D array
+ /// 2D array of same dimensions but with double elements
+ public static double[,] ToDoubleArray2D(Array array)
+ {
+ if (array == null)
+ throw new ArgumentNullException("array");
+ if (array.Rank != 2)
+ throw new InvalidOperationException("Source array is not 2D");
+ var elemType = array.GetType().GetElementType();
+ if (elemType == typeof(double))
+ return (double[,])array;
+ if (elemType == typeof(float))
+ return ArrayExtensions.ToDoubleArray((float[,])array);
+ else if (elemType == typeof(int))
+ return ArrayExtensions.ToDoubleArray((int[,])array);
+ else if (elemType == typeof(bool))
+ return ArrayExtensions.ToDoubleArray((bool[,])array);
+ else
+ throw new NotSupportedException("Conversions of 2D arrays of " + elemType.Name + " to double[,] is not supported");
+ }
+
+ #endregion
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Common/DataRect.cs b/InteractiveDataDisplay_OneDll/src/Common/DataRect.cs
new file mode 100644
index 000000000..d29b90cf5
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Common/DataRect.cs
@@ -0,0 +1,232 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System.Windows;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Describes a width, a height and location of a rectangle.
+ /// This type is very similar to , but has one important difference:
+ /// while describes a rectangle in screen coordinates, where y axis
+ /// points to bottom (that's why 's Bottom property is greater than Top).
+ /// This type describes rectange in usual coordinates, and y axis point to top.
+ ///
+ public struct DataRect
+ {
+ private Range x;
+ private Range y;
+
+ ///
+ /// Gets the empty .
+ ///
+ public static readonly DataRect Empty =
+ new DataRect(Range.Empty, Range.Empty);
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// Left value of DataRect.
+ /// Bottom value of DataRect.
+ /// Right value of DataRect.
+ /// Top value of DataRect.
+ public DataRect(double minX, double minY, double maxX, double maxY)
+ : this()
+ {
+ X = new Range(minX, maxX);
+ Y = new Range(minY, maxY);
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// Horizontal range
+ /// Vertical range
+ public DataRect(Range horizontal, Range vertical)
+ : this()
+ {
+ X = horizontal; Y = vertical;
+ }
+
+ ///
+ /// Gets of Sets horizontal range
+ ///
+ public Range X
+ {
+ get { return x; }
+ set { x = value; }
+ }
+
+ ///
+ /// Gets or sets vertical range
+ ///
+ public Range Y
+ {
+ get { return y; }
+ set { y = value; }
+ }
+
+ ///
+ /// Gets the width of .
+ ///
+ public double Width
+ {
+ get
+ {
+ return X.Max - X.Min;
+ }
+ }
+
+ ///
+ /// Gets the height of .
+ ///
+ public double Height
+ {
+ get
+ {
+ return Y.Max - Y.Min;
+ }
+ }
+
+ ///
+ /// Gets the left.
+ ///
+ /// The left.
+ public double XMin
+ {
+ get
+ {
+ return X.Min;
+ }
+ }
+
+ ///
+ /// Gets the right.
+ ///
+ /// The right.
+ public double XMax
+ {
+ get
+ {
+ return X.Max;
+ }
+ }
+
+ ///
+ /// Gets the bottom.
+ ///
+ /// The bottom.
+ public double YMin
+ {
+ get
+ {
+ return Y.Min;
+ }
+ }
+
+ ///
+ /// Gets the top.
+ ///
+ /// The top.
+ public double YMax
+ {
+ get
+ {
+ return Y.Max;
+ }
+ }
+
+ ///
+ /// Gets a center point of a rectangle.
+ ///
+ public Point Center
+ {
+ get
+ {
+ return new Point((X.Max + X.Min) / 2.0, (Y.Max + Y.Min) / 2.0);
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether this instance is empty.
+ ///
+ /// true if this instance is empty; otherwise, false.
+ public bool IsEmpty
+ {
+ get
+ {
+ return X.IsEmpty || Y.IsEmpty;
+ }
+ }
+
+
+ ///
+ /// Updates current instance of with minimal DataRect
+ /// which vertical range will contain current vertical range and x value and
+ /// horizontal range will contain current horizontal range and y value
+ ///
+ /// Value, which will be used for surrond of horizontal range
+ /// Value, which will be used for surrond of vertical range
+ public void Surround(double x, double y)
+ {
+ this.x.Surround(x);
+ this.y.Surround(y);
+ }
+
+ ///
+ /// Updates current instance of with minimal DataRect which will contain current DataRect and specified DataRect
+ ///
+ /// DataRect, which will be used for surrond of current instance of
+ public void Surround(DataRect rect)
+ {
+ this.x.Surround(rect.X);
+ this.y.Surround(rect.Y);
+ }
+
+ ///
+ /// Updates horizontal range of current instance of with minimal range which will contain both horizontal range of current DataRect and specified value
+ ///
+ /// Value, which will be used for surrond of horizontal range of current instance of
+ public void XSurround(double x)
+ {
+ this.x.Surround(x);
+ }
+
+ ///
+ /// Updates horizontal range of current instance of with minimal range which will contain both horizontal range of current DataRect and specified range
+ ///
+ /// Range, which will be used for surrond of horizontal range of current instance of
+ public void XSurround(Range x)
+ {
+ this.x.Surround(x);
+ }
+
+ ///
+ /// Updates vertical range of current instance of with minimal range which will contain both vertical range of current DataRect and specified value
+ ///
+ /// Value, which will be used for surrond of vertical range of current instance of
+ public void YSurround(double y)
+ {
+ this.y.Surround(y);
+ }
+
+ ///
+ /// Updates vertical range of current instance of with minimal range which will contain both vertical range of current DataRect and specified range
+ ///
+ /// Range, which will be used for surrond of vertical range of current instance of
+ public void YSurround(Range y)
+ {
+ this.y.Surround(y);
+ }
+
+ ///
+ /// Returns a string that represents the current instance of .
+ ///
+ /// String that represents the current instance of
+ public override string ToString()
+ {
+ return "{" + X.ToString() + " " + Y.ToString() + "}";
+ }
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Common/IPalette.cs b/InteractiveDataDisplay_OneDll/src/Common/IPalette.cs
new file mode 100644
index 000000000..ae73246b9
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Common/IPalette.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+// Copyright © 2010 Microsoft Corporation, All Rights Reserved.
+// This code released under the terms of the Microsoft Research License Agreement (MSR-LA, http://sds.codeplex.com/License)
+using System.Windows.Media;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Represents a color palette, which can convert double values to colors.
+ ///
+ public interface IPalette
+ {
+ ///
+ /// Gets a color for the specified value.
+ ///
+ /// A value from the .
+ /// A color.
+ Color GetColor(double value);
+
+ ///
+ /// Gets the value indicating whether the contains absolute values
+ /// or it is relative ([0...1]).
+ ///
+ bool IsNormalized { get; }
+
+ ///
+ /// Gets the range on which palette is defined.
+ ///
+ Range Range { get; }
+ }
+}
+
+
diff --git a/InteractiveDataDisplay_OneDll/src/Common/MathHelper.cs b/InteractiveDataDisplay_OneDll/src/Common/MathHelper.cs
new file mode 100644
index 000000000..bb7ed7621
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Common/MathHelper.cs
@@ -0,0 +1,103 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Windows;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Helper class for mathematical calculations.
+ ///
+ public static class MathHelper
+ {
+ ///
+ /// Verifies whether the value is NaN of Infinity.
+ ///
+ /// The value to varify.
+ /// True if the value is not NaN and is Infinity, false otherwise.
+ public static bool IsFinite(this double value)
+ {
+ return !Double.IsNaN(value) && !Double.IsInfinity(value);
+ }
+
+ ///
+ /// Clamps specified long value to specified interval.
+ ///
+ /// Value to clamp.
+ /// Minimum of the interval.
+ /// Maximum of the interval.
+ /// Long value in range [min, max].
+ public static long Clamp(long value, long min, long max)
+ {
+ return Math.Max(min, Math.Min(value, max));
+ }
+
+ ///
+ /// Clamps specified double value to specified interval.
+ ///
+ /// Value to clamp.
+ /// Minimum of the interval.
+ /// Maximum of the interval.
+ /// Double value in range [min, max].
+ public static double Clamp(double value, double min, double max)
+ {
+ return Math.Max(min, Math.Min(value, max));
+ }
+
+ /// Clamps specified double value to [0,1].
+ /// Value to clamp.
+ /// Double value in range [0, 1].
+ public static double Clamp(double value)
+ {
+ return Math.Max(0, Math.Min(value, 1));
+ }
+
+ ///
+ /// Clamps specified integer value to specified interval.
+ ///
+ /// Value to clamp.
+ /// Minimum of the interval.
+ /// Maximum of the interval.
+ /// Integer value in range [min, max].
+ public static int Clamp(int value, int min, int max)
+ {
+ return Math.Max(min, Math.Min(value, max));
+ }
+
+ ///
+ /// Returns by four given coordinates of corner points.
+ ///
+ /// X coordinate on a left-top corner.
+ /// Y coordinate on a left-top corner.
+ /// X coordinate on a right-bottom corner.
+ /// Y coordinate on a right-bottom corner.
+ ///
+ public static Rect CreateRectByPoints(double minX, double minY, double maxX, double maxY)
+ {
+ return new Rect(new Point(minX, minY), new Point(maxX, maxY));
+ }
+
+ ///
+ /// Returns by given point of a center and .
+ ///
+ /// Point of a center of a rectangle.
+ /// Size of a rectangle.
+ /// Rect.
+ public static Rect CreateRectFromCenterSize(Point center, Size size)
+ {
+ return new Rect(center.X - size.Width / 2, center.Y - size.Height / 2, size.Width, size.Height);
+ }
+
+ ///
+ /// Converts an angle in radians to the angle in degrees.
+ ///
+ /// Angle in radians.
+ /// Angle in degrees.
+ public static double ToDegrees(this double angleInRadians)
+ {
+ return angleInRadians * 180 / Math.PI;
+ }
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Common/Palette.cs b/InteractiveDataDisplay_OneDll/src/Common/Palette.cs
new file mode 100644
index 000000000..9be780330
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Common/Palette.cs
@@ -0,0 +1,897 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+// Copyright © 2010 Microsoft Corporation, All Rights Reserved.
+// This code released under the terms of the Microsoft Research License Agreement (MSR-LA, http://sds.codeplex.com/License)
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows.Media;
+using System.Globalization;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Represents a point within the range of a palette, containing colors on the left and rigth sides.
+ ///
+ public class PalettePoint
+ {
+ private Color rightColor, leftColor;
+ private double x;
+
+ internal PalettePoint()
+ {}
+
+ ///
+ /// Initializes a new instance of class.
+ ///
+ /// Coordinate of a point.
+ /// Color on a right side.
+ /// Color on a left side.
+ public PalettePoint(double x, Color right, Color left)
+ {
+ this.rightColor = right;
+ this.leftColor = left;
+ this.x = x;
+ }
+
+ ///
+ /// Gets the color on a right side of a point.
+ ///
+ public Color RightColor
+ {
+ internal set { rightColor = value; }
+ get { return rightColor; }
+ }
+ ///
+ /// Gets the color on a left side of a point.
+ ///
+ public Color LeftColor
+ {
+ internal set { leftColor = value; }
+ get { return leftColor; }
+ }
+ ///
+ /// Gets the coordinate of a point.
+ ///
+ public double X
+ {
+ internal set { x = value; }
+ get { return x; }
+ }
+
+ ///
+ /// Returns a string that represents the current palette point with information of coordinate and colors.
+ ///
+ public override string ToString()
+ {
+ return String.Format(CultureInfo.InvariantCulture, "{0} - left: {1}, right: {2}", x, leftColor, rightColor);
+ }
+ }
+
+ ///
+ /// Defines mapping between color and double value.
+ ///
+ public class Palette : IPalette
+ {
+ private bool isNormalized;
+ private Range range;
+ private PalettePoint[] points;
+
+ ///
+ /// Predefined palette "Blue, Green, Red".
+ ///
+ public static Palette Heat = new Palette(true, new Range(0, 1),
+ new PalettePoint[3]{
+ new PalettePoint(0.0, Colors.Blue, Colors.Blue),
+ new PalettePoint(0.5, Color.FromArgb(255, 0, 255, 0), Color.FromArgb(255, 0, 255, 0)),
+ new PalettePoint(1.0, Colors.Red, Colors.Red)
+ });
+
+ // See http://www.geos.ed.ac.uk/it/howto/GMT/CPT/palettes.html
+ // public readonly static Palette Topo = Palette.Parse("#C977D9,#A18AE6,#8AA2E6,#8BD1E7,#8AF3CF,#85F38E,#EDE485,#F0B086,#DE9F8B,#74A3B3=0.5,#99CC70,#DCD68E,#BDF385,#EDDFAD,#F7E8CA,#FFF9F3,#FFF9F6,#FFFBF9,#FFFCFA,White");
+
+ private Palette()
+ {
+ }
+
+ ///
+ /// Initializes an instance of class.
+ ///
+ /// Indicated whether palette is absolute or normalized.
+ /// If true, the actual range of palette is always [0...1].
+ /// Range of palette.
+ /// An array of color points.
+ ///
+ /// If is true, value of the
+ /// is ignored and actual range is always [0...1].
+ /// The array is cloned before use and therefore can be modified
+ /// after the constructor is completed without any effect on the palette instance.
+ ///
+ ///
+ public Palette(bool isNormalized, Range range, PalettePoint[] points)
+ {
+ this.isNormalized = isNormalized;
+ if (isNormalized)
+ this.range = new Range(0, 1);
+ else
+ this.range = new Range(range.Min, range.Max);
+
+ if (points == null) throw new ArgumentNullException("points");
+ if (points.Length < 2) throw new ArgumentException("Palette should have at least two points");
+ this.points = (PalettePoint[])points.Clone();
+ }
+
+ ///
+ /// Initializes new instance of class on a basis of existing palette's colors.
+ ///
+ /// Indicated whether palette is absolute or normalized.
+ /// If true, the actual range of palette is always [0...1].
+ /// Range of palette.
+ /// A palette to construct a new one.
+ public Palette(bool isNormalized, Range range, Palette palette)
+ {
+ if (palette == null) throw new ArgumentNullException("palette");
+ this.isNormalized = isNormalized;
+ if (isNormalized)
+ this.range = new Range(0, 1);
+ else
+ this.range = range;
+
+ if (palette.IsNormalized == isNormalized)
+ {
+ points = (PalettePoint[])palette.points.Clone();
+ }
+ else
+ {
+ var srcRange = palette.Range;
+ double alpha, beta;
+ if (isNormalized) // from absolute to normalized
+ {
+ alpha = 1.0 / (srcRange.Max - srcRange.Min);
+ beta = -srcRange.Min * alpha;
+ }
+ else // from normalized to absolute
+ {
+ alpha = range.Max - range.Min;
+ beta = range.Min;
+ }
+ points = palette.points.Select(pp => new PalettePoint(pp.X * alpha + beta, pp.RightColor, pp.LeftColor)).ToArray();
+ }
+ }
+
+ ///
+ /// Gets the points with colors describing the palette.
+ ///
+ public PalettePoint[] Points
+ {
+ get { return points.Clone() as PalettePoint[]; }
+ }
+
+ ///
+ /// Parses the palette from the string. See remarks for details.
+ ///
+ /// A string to parse from.
+ ///
+ /// String can contain: names of colors ( or hexadecimal representation #AARRGGBB),
+ /// double values and separators (equal symbol or comma) to separate colors and values from each other.
+ /// Comma is used for gradient colors, equal symbol - for initializing a color of specified value.
+ /// Palette can be normalized or absolute.
+ /// Absolute palette maps entire numbers to colors exactly as numbers are specified. For examples,
+ /// palette '-10=Blue,Green,Yellow,Red=10' maps values -10 and below to Blue, 10 and above to red.
+ /// Normalized palette maps minimum value to left palette color, maximum value to right palette color. In normalized palette
+ /// no numbers on left and right sides are specified.
+ /// Palettes can contain regions of constant color. For example, palette 'Blue=0.25=Yellow=0.5=#00FF00=0.75=Orange' maps first quarter of
+ /// values to blue, second quarter to yellow and so on.
+ ///
+ /// A palette that specified string describes.
+ public static Palette Parse(string value)
+ {
+ bool isNormalized = true;
+ Range range;
+ List points = new List();
+
+ if (value == null) value = String.Empty;
+ if (String.IsNullOrEmpty(value))
+ return new Palette(true, new Range(0, 1), new PalettePoint[2]{
+ new PalettePoint(0.0, Colors.White, Colors.White),
+ new PalettePoint(1.0, Colors.Black, Colors.Black)});
+ Lexer lexer = new Lexer(value);
+ int state = -1;
+ double lastNumber;
+ if (lexer.ReadNext())
+ {
+ points.Add(new PalettePoint(0.0, Colors.White, Colors.White));
+ if (lexer.CurrentLexeme == Lexer.LexemeType.Number)
+ {
+ points[points.Count - 1].X = lexer.ValueNumber;
+ isNormalized = false;
+ if (lexer.ReadNext() && lexer.CurrentLexeme != Lexer.LexemeType.Separator)
+ throw new PaletteFormatException(lexer.Position, "separator expected");
+ if (lexer.ReadNext() && lexer.CurrentLexeme != Lexer.LexemeType.Color)
+ throw new PaletteFormatException(lexer.Position, "color expected");
+ }
+ if (lexer.CurrentLexeme == Lexer.LexemeType.Color)
+ {
+ points[points.Count - 1].RightColor = lexer.ValueColor;
+ points[points.Count - 1].LeftColor = lexer.ValueColor;
+ points.Add(new PalettePoint(points[0].X, lexer.ValueColor, lexer.ValueColor));
+ }
+ else
+ throw new PaletteFormatException(lexer.Position, "wrong lexeme");
+ }
+ lastNumber = points[0].X;
+ while (lexer.ReadNext())
+ {
+ if (lexer.CurrentLexeme == Lexer.LexemeType.Separator)
+ {
+ if (lexer.ValueSeparator == Lexer.Separator.Equal)
+ {
+ if (lexer.ReadNext())
+ {
+ if (lexer.CurrentLexeme == Lexer.LexemeType.Number)
+ {
+ if (lexer.ValueNumber < lastNumber)
+ throw new PaletteFormatException(lexer.Position, "number is less than previous");
+ lastNumber = lexer.ValueNumber;
+ if (state == -1)
+ {
+ //x1 = color = x2
+ points[points.Count - 1].X = lexer.ValueNumber;
+ state = 1;
+ }
+ else if (state == 0)
+ {
+ //color = x
+ points[points.Count - 1].X = lexer.ValueNumber;
+ state = 2;
+ }
+ else
+ throw new PaletteFormatException(lexer.Position, "wrong lexeme");
+ }
+ else if (lexer.CurrentLexeme == Lexer.LexemeType.Color)
+ {
+ if (state == 1 || state == 2)
+ {
+ //x = color (,x=color || color1=x=color2)
+ points[points.Count - 1].RightColor = lexer.ValueColor;
+ state = -1;
+ }
+ else if (state == 0 || state == -1)
+ {
+ //color1 = color2
+ points[points.Count - 1].X = points[0].X - 1;
+ points[points.Count - 1].RightColor = lexer.ValueColor;
+ state = -1;
+ }
+ else
+ throw new PaletteFormatException(lexer.Position, "wrong lexeme");
+ }
+ else
+ throw new PaletteFormatException(lexer.Position, "wrong lexeme");
+ }
+ }
+ else if (lexer.ValueSeparator == Lexer.Separator.Comma)
+ {
+ if (lexer.ReadNext())
+ {
+ if (state == 1 || state == -1 || state == 2)
+ {
+ if (lexer.CurrentLexeme == Lexer.LexemeType.Number)
+ {
+ if (lexer.ValueNumber <= lastNumber)
+ throw new PaletteFormatException(lexer.Position, "number is less than previous");
+ lastNumber = lexer.ValueNumber;
+ //x1 = color, x2
+ if (lexer.ReadNext() && lexer.CurrentLexeme == Lexer.LexemeType.Separator && lexer.ValueSeparator == Lexer.Separator.Equal)
+ {
+ if (lexer.ReadNext() && lexer.CurrentLexeme == Lexer.LexemeType.Color)
+ {
+ if (state != -1)
+ points.Add(new PalettePoint(lexer.ValueNumber, lexer.ValueColor, lexer.ValueColor));
+ else
+ {
+ points[points.Count - 1].X = lexer.ValueNumber;
+ points[points.Count - 1].RightColor = lexer.ValueColor;
+ points[points.Count - 1].LeftColor = lexer.ValueColor;
+ }
+ state = -1;
+ }
+ else
+ throw new PaletteFormatException(lexer.Position, "color expected");
+ }
+ else
+ throw new PaletteFormatException(lexer.Position, "wrong lexeme");
+ }
+ else if (lexer.CurrentLexeme == Lexer.LexemeType.Color)
+ {
+ // x = color1, color2
+ if (state == -1)
+ points.RemoveAt(points.Count - 1);
+ state = 0;
+ }
+ else
+ throw new PaletteFormatException(lexer.Position, "wrong lexeme");
+ }
+
+ else if (state == 0)
+ {
+ if (lexer.CurrentLexeme == Lexer.LexemeType.Number)
+ {
+ if (lexer.ValueNumber <= lastNumber)
+ throw new PaletteFormatException(lexer.Position, "number is less than previous");
+ lastNumber = lexer.ValueNumber;
+ //color, x
+ points[points.Count - 1].X = points[0].X - 1;
+ if (lexer.ReadNext() && lexer.CurrentLexeme == Lexer.LexemeType.Separator && lexer.ValueSeparator == Lexer.Separator.Equal)
+ {
+ if (lexer.ReadNext() && lexer.CurrentLexeme == Lexer.LexemeType.Color)
+ {
+ points.Add(new PalettePoint(lexer.ValueNumber, lexer.ValueColor, lexer.ValueColor));
+ state = -1;
+ }
+ else
+ throw new PaletteFormatException(lexer.Position, "color expected");
+ }
+ else
+ throw new PaletteFormatException(lexer.Position, "wrong lexeme");
+ }
+ else if (lexer.CurrentLexeme == Lexer.LexemeType.Color)
+ {
+ //color1, color2
+ points[points.Count - 1].X = points[0].X - 1;
+ state = 0;
+ }
+ else
+ throw new PaletteFormatException(lexer.Position, "wrong lexeme");
+ }
+ }
+ }
+ if (state == -1)
+ points.Add(new PalettePoint(points[0].X, lexer.ValueColor, lexer.ValueColor));
+ else if (state == 0)
+ points.Add(new PalettePoint(points[0].X, lexer.ValueColor, lexer.ValueColor));
+ }
+ else
+ throw new PaletteFormatException(lexer.Position, "separator expected");
+ }
+
+ if (lexer.CurrentLexeme == Lexer.LexemeType.Separator)
+ throw new PaletteFormatException(lexer.Position, "wrong lexeme");
+ if ((lexer.CurrentLexeme == Lexer.LexemeType.Number && isNormalized) ||
+ (lexer.CurrentLexeme == Lexer.LexemeType.Color && !isNormalized))
+ throw new PaletteFormatException(lexer.Position, "wrong ending");
+ if (isNormalized)
+ {
+ points[points.Count - 1].X = 1.0;
+ if (points[points.Count - 1].X < points[points.Count - 2].X)
+ throw new PaletteFormatException(lexer.Position, "number is less than previous");
+ }
+ points[points.Count - 1].RightColor = points[points.Count - 1].LeftColor;
+ if (points[0].X >= points[points.Count - 1].X)
+ throw new PaletteFormatException(lexer.Position, "wrong range of palette");
+ range = new Range(points[0].X, points[points.Count - 1].X);
+
+ int start = 1;
+ int count = 0;
+ for (int i = 1; i < points.Count; i++)
+ {
+ if (points[i].X == points[0].X - 1)
+ {
+ if (count == 0) start = i;
+ count++;
+ }
+ else if (count != 0)
+ {
+ double res_x = (points[start + count].X - points[start - 1].X) / (count + 1);
+ for (int j = 0; j < count; j++)
+ points[start + j].X = points[start - 1].X + res_x * (j + 1);
+ count = 0;
+ start = 1;
+ }
+ }
+
+ return new Palette(isNormalized, range, points.ToArray());
+ }
+
+ private static string ColorToString(Color color)
+ {
+ Type colors = typeof(Colors);
+ foreach (var property in colors.GetProperties())
+ {
+ if (((Color)property.GetValue(null, null)) == color)
+ return property.Name;
+ }
+ if (color.A == 0xff)
+ return string.Format(CultureInfo.InvariantCulture, "#{0:X2}{1:X2}{2:X2}", color.R, color.G, color.B);
+ return string.Format(CultureInfo.InvariantCulture, "#{0:X2}{1:X2}{2:X2}{3:X2}", color.A, color.R, color.G, color.B);
+ }
+
+ private static string PositionToString(double x)
+ {
+ return x.ToString(CultureInfo.InvariantCulture);
+ }
+
+ ///
+ /// Gets a string that describes current palette.
+ ///
+ /// String that describes palette.
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ int k = 0;
+
+ if (!IsNormalized)
+ sb.Append(PositionToString(points[k].X)).Append('=');
+ sb.Append(ColorToString(points[k].RightColor));
+ k++;
+ for (; k < points.Length - 1; k++)
+ {
+ if (points[k].LeftColor == points[k - 1].RightColor)
+ {
+ sb.Append('=').Append(PositionToString(points[k].X));
+ if (points[k].RightColor != points[k].LeftColor ||
+ (points[k].RightColor == points[k].LeftColor && points[k + 1].LeftColor == points[k].RightColor))
+ {
+ sb.Append('=');
+ sb.Append(ColorToString(points[k].RightColor));
+ }
+ }
+ else
+ {
+ sb.Append(',');
+ if (points[k].RightColor == points[k].LeftColor && points[k].LeftColor == points[k + 1].LeftColor)
+ {
+ sb.Append(PositionToString(points[k].X)).Append('=');
+ sb.Append(ColorToString(points[k].LeftColor));
+ }
+ else
+ {
+ sb.Append(ColorToString(points[k].LeftColor)).Append('=').Append(PositionToString(points[k].X));
+ if (points[k].RightColor == points[k + 1].LeftColor ||
+ points[k].RightColor != points[k].LeftColor)
+ {
+ sb.Append('=');
+ sb.Append(ColorToString(points[k].RightColor));
+ }
+ }
+ }
+ }
+ k = points.Length - 1;
+ if (points[k].LeftColor != points[k - 1].RightColor)
+ {
+ sb.Append(',');
+ sb.Append(ColorToString(points[k].LeftColor));
+ }
+ if (!IsNormalized)
+ sb.Append('=').Append(PositionToString(points[k].X));
+ return sb.ToString();
+ }
+
+ #region IPalette Members
+
+ ///
+ /// Gets a color for the specified value. Note that palette uses HSL interpolation of colors.
+ ///
+ /// A double value from .
+ /// A color for specific double value.
+ public Color GetColor(double value)
+ {
+ Color color = new Color();
+ if (IsNormalized && (value > 1 || value < 0))
+ throw new ArgumentException("Wrong value for normalized palette");
+ else
+ {
+ if (value <= points[0].X)
+ {
+ color.A = points[0].LeftColor.A;
+ color.R = points[0].LeftColor.R;
+ color.G = points[0].LeftColor.G;
+ color.B = points[0].LeftColor.B;
+ }
+ else if (value >= points[points.Length - 1].X)
+ {
+ color.A = points[points.Length - 1].RightColor.A;
+ color.R = points[points.Length - 1].RightColor.R;
+ color.G = points[points.Length - 1].RightColor.G;
+ color.B = points[points.Length - 1].RightColor.B;
+ }
+ else
+ for (int i = 0; i < points.Length - 1; i++)
+ {
+ var p1 = points[i];
+ var p2 = points[i + 1];
+ if (value >= p1.X && value <= p2.X)
+ {
+ var c1 = p1.RightColor;
+ var c2 = p2.LeftColor;
+ double alpha = (value - points[i].X) / (p2.X - p1.X);
+
+ HSLColor cHSL1 = new HSLColor(c1);
+ HSLColor cHSL2 = new HSLColor(c2);
+
+ if (Math.Abs(cHSL2.H - cHSL1.H) > 3)
+ {
+ if (cHSL1.H < cHSL2.H)
+ cHSL1 = new HSLColor(cHSL1.A, cHSL1.H + 6, cHSL1.S, cHSL1.L);
+ else if (cHSL1.H > cHSL2.H)
+ cHSL2 = new HSLColor(cHSL2.A, cHSL2.H + 6, cHSL2.S, cHSL2.L);
+ }
+
+ HSLColor cHSL = new HSLColor(cHSL1.A + ((cHSL2.A - cHSL1.A) * alpha),
+ cHSL1.H + ((cHSL2.H - cHSL1.H) * alpha),
+ cHSL1.S + ((cHSL2.S - cHSL1.S) * alpha),
+ cHSL1.L + ((cHSL2.L - cHSL1.L) * alpha));
+
+ if (cHSL.H >= 6)
+ cHSL = new HSLColor(cHSL.A, cHSL.H - 6, cHSL.S, cHSL.L);
+ color = cHSL.ConvertToRGB();
+ break;
+ }
+ }
+ }
+ return color;
+ }
+
+ ///
+ /// Gets the value indicating whether the is absolute or relative ([0, 1]).
+ ///
+ public bool IsNormalized
+ {
+ get { return isNormalized; }
+ }
+
+ ///
+ /// Gets the range on which palette is defined.
+ ///
+ public Range Range
+ {
+ get { return range; }
+ }
+ #endregion
+
+ ///
+ /// Determines whether the specified palette is equal to the current instance.
+ ///
+ /// The palette to compare with current.
+ /// True if the objects are equal, false otherwise.
+ public override bool Equals(object obj)
+ {
+ if (obj == null) return false;
+ Palette p = obj as Palette;
+ if (p == null) return false;
+ if (isNormalized != p.isNormalized) return false;
+ if (range.Min != p.range.Min || range.Max != p.range.Max) return false;
+ int n = this.points.Length;
+ if (n != p.points.Length) return false;
+ for (int i = 0; i < n; i++)
+ {
+ var l = points[i];
+ var r = p.points[i];
+ if (l.X != r.X || l.LeftColor != r.LeftColor || l.RightColor != r.RightColor)
+ return false;
+ }
+ return true;
+ }
+
+ ///
+ /// Serves as a hash function for a palette type.
+ ///
+ ///
+ public override int GetHashCode()
+ {
+ int hash = isNormalized.GetHashCode() ^ range.GetHashCode();
+ for (int i = 0; i < points.Length; i++)
+ {
+ var l = points[i];
+ hash ^= l.X.GetHashCode() ^ l.LeftColor.GetHashCode() ^ l.RightColor.GetHashCode();
+ }
+ return hash;
+ }
+ }
+
+ internal class Lexer
+ {
+ public enum Separator { Equal, Comma };
+ public enum LexemeType { Color, Separator, Number };
+
+ LexemeType currentLexem;
+ Color valueColor;
+ double valueNumber;
+ Separator valueSeparator;
+
+ string paletteString;
+ int position;
+
+ public LexemeType CurrentLexeme
+ {
+ get { return currentLexem; }
+ }
+ public Color ValueColor
+ {
+ get { return valueColor; }
+ }
+ public double ValueNumber
+ {
+ get { return valueNumber; }
+ }
+ public Separator ValueSeparator
+ {
+ get { return valueSeparator; }
+ }
+ public int Position
+ {
+ get { return position; }
+ }
+
+ public Lexer(string value)
+ {
+ paletteString = value;
+ position = 0;
+ }
+
+ public bool ReadNext()
+ {
+ if (position >= paletteString.Length)
+ return false;
+ while (paletteString[position] == ' ') position++;
+
+ if (paletteString[position] == '#' || Char.IsLetter(paletteString[position]))
+ {
+ currentLexem = LexemeType.Color;
+ int start = position;
+ while (position < paletteString.Length && paletteString[position] != ' ' &&
+ paletteString[position] != '=' && paletteString[position] != ',')
+ {
+ position++;
+ }
+ string color = paletteString.Substring(start, position - start);
+ valueColor = GetColorFromString(color);
+ }
+ else if (paletteString[position] == '=' || paletteString[position] == ',')
+ {
+ currentLexem = LexemeType.Separator;
+ if (paletteString[position] == '=') valueSeparator = Separator.Equal;
+ else valueSeparator = Separator.Comma;
+ position++;
+ }
+ else
+ {
+ currentLexem = LexemeType.Number;
+ int start = position;
+ while (position < paletteString.Length && paletteString[position] != ' ' &&
+ paletteString[position] != '=' && paletteString[position] != ',')
+ {
+ position++;
+ }
+ string number = paletteString.Substring(start, position - start);
+ valueNumber = Double.Parse(number, CultureInfo.InvariantCulture);
+ }
+ return true;
+ }
+
+ private Color GetColorFromString(string str)
+ {
+ Color color = new Color();
+
+ if (Char.IsLetter(str[0]))
+ {
+ bool isNamed = false;
+ Type colors = typeof(Colors);
+ foreach (var property in colors.GetProperties())
+ {
+ if (property.Name == str)
+ {
+ color = (Color)property.GetValue(null, null);
+ isNamed = true;
+ }
+ }
+ if (!isNamed)
+ throw new PaletteFormatException(Position, "wrong name of color");
+ }
+ else if (str[0] == '#')
+ {
+ if (str.Length == 7)
+ {
+ color.A = 255;
+ color.R = Convert.ToByte(str.Substring(1, 2), 16);
+ color.G = Convert.ToByte(str.Substring(3, 2), 16);
+ color.B = Convert.ToByte(str.Substring(5, 2), 16);
+ }
+ else if (str.Length == 9)
+ {
+ color.A = Convert.ToByte(str.Substring(1, 2), 16);
+ color.R = Convert.ToByte(str.Substring(3, 2), 16);
+ color.G = Convert.ToByte(str.Substring(5, 2), 16);
+ color.B = Convert.ToByte(str.Substring(7, 2), 16);
+ }
+ else throw new PaletteFormatException(Position, "wrong name of color");
+ }
+ else throw new PaletteFormatException(Position, "wrong name of color");
+
+ return color;
+ }
+ }
+
+ ///
+ /// An exception that is thrown when an argument does not meet the parameter specifications
+ /// of class methods.
+ ///
+ public class PaletteFormatException : FormatException
+ {
+ ///
+ /// Initializes a new instance of class.
+ ///
+ /// A position in a string in which an exception occured.
+ /// A message to show.
+ public PaletteFormatException(int position, string message) :
+ base(String.Format(CultureInfo.InvariantCulture, "Incorrect palette string at char {0}: {1}", position, message)) { }
+
+ ///
+ /// Initializes a new instance of class with default message.
+ ///
+ public PaletteFormatException() { }
+
+ ///
+ /// Initializes a new instance of class.
+ ///
+ /// A message to show.
+ public PaletteFormatException(string message)
+ : base(message) { }
+
+ ///
+ /// Initializes a new instance of class.
+ ///
+ /// A message to show.
+ /// An occured exception.
+ public PaletteFormatException(string message, Exception innerException)
+ : base(message, innerException) { }
+ }
+
+ internal class HSLColor
+ {
+ private double h;
+ private double s;
+ private double l;
+ private double a;
+
+ public HSLColor(double a, double h, double s, double l)
+ {
+ this.a = a;
+ this.h = h;
+ this.s = s;
+ this.l = l;
+ }
+
+ public HSLColor(Color color)
+ {
+ this.a = color.A / 255.0;
+
+ double r = color.R / 255.0;
+ double g = color.G / 255.0;
+ double b = color.B / 255.0;
+
+ double maxcolor = Math.Max(r, g);
+ maxcolor = Math.Max(maxcolor, b);
+ double mincolor = Math.Min(r, g);
+ mincolor = Math.Min(mincolor, b);
+
+ this.l = (maxcolor + mincolor) / 2.0;
+
+ if (maxcolor == mincolor)
+ this.s = 0.0;
+ else
+ {
+ if (this.l < 0.5)
+ s = (maxcolor - mincolor) / (maxcolor + mincolor);
+ else
+ s = (maxcolor - mincolor) / (2.0 - maxcolor - mincolor);
+ }
+ if (maxcolor == mincolor)
+ this.h = 0;
+ else if (maxcolor == r)
+ {
+ if (g >= b)
+ this.h = (g - b) / (maxcolor - mincolor);
+ else
+ this.h = (g - b) / (maxcolor - mincolor) + 6.0;
+ }
+ else if (maxcolor == g)
+ this.h = 2.0 + (b - r) / (maxcolor - mincolor);
+ else if (maxcolor == b)
+ this.h = 4.0 + (r - g) / (maxcolor - mincolor);
+ }
+
+ public Color ConvertToRGB()
+ {
+ Color color = new Color();
+ color.A = (byte)(this.a * 255.0);
+
+ double c = (1.0 - Math.Abs(2.0 * this.l - 1.0)) * this.s;
+ double x = c * (1.0 - Math.Abs(this.h % 2.0 - 1.0));
+
+ if (this.h < 1 && this.h >= 0)
+ {
+ color.R = Convert.ToByte(c * 255.0);
+ color.G = Convert.ToByte(x * 255.0);
+ color.B = 0;
+ }
+ if (this.h < 2 && this.h >= 1)
+ {
+ color.R = Convert.ToByte(x * 255.0);
+ color.G = Convert.ToByte(c * 255.0);
+ color.B = 0;
+ }
+ if (this.h < 3 && this.h >= 2)
+ {
+ color.R = 0;
+ color.G = Convert.ToByte(c * 255.0);
+ color.B = Convert.ToByte(x * 255.0);
+ }
+ if (this.h < 4 && this.h >= 3)
+ {
+ color.R = 0;
+ color.G = Convert.ToByte(x * 255.0);
+ color.B = Convert.ToByte(c * 255.0);
+ }
+ if (this.h < 5 && this.h >= 4)
+ {
+ color.R = Convert.ToByte(x * 255.0);
+ color.G = 0;
+ color.B = Convert.ToByte(c * 255.0);
+ }
+ if (this.h < 6 && this.h >= 5)
+ {
+ color.R = Convert.ToByte(c * 255.0);
+ color.G = 0;
+ color.B = Convert.ToByte(x * 255.0);
+ }
+
+ double m = (this.l - c / 2.0) * 255.0;
+ double temp = color.R + m;
+ if (temp > 255)
+ color.R = 255;
+ else if (temp < 0)
+ color.R = 0;
+ else
+ color.R = Convert.ToByte(temp);
+
+ temp = color.G + m;
+ if (temp > 255)
+ color.G = 255;
+ else if (temp < 0)
+ color.G = 0;
+ else
+ color.G = Convert.ToByte(color.G + m);
+
+ temp = color.B + m;
+ if (temp > 255)
+ color.B = 255;
+ else if (temp < 0)
+ color.B = 0;
+ else
+ color.B = Convert.ToByte(color.B + m);
+
+ return color;
+ }
+
+ public double A
+ {
+ get { return a; }
+ }
+ public double H
+ {
+ get { return h; }
+ }
+ public double S
+ {
+ get { return s; }
+ }
+ public double L
+ {
+ get { return l; }
+ }
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Common/Range.cs b/InteractiveDataDisplay_OneDll/src/Common/Range.cs
new file mode 100644
index 000000000..c8c07af34
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Common/Range.cs
@@ -0,0 +1,173 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System.Diagnostics;
+using System.Globalization;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Represents ranges for double type.
+ ///
+ public struct Range
+ {
+ double minimum;
+ double maximum;
+
+ ///
+ /// Gets the minimum value of current range.
+ ///
+ public double Min
+ {
+ get { return minimum; }
+ set { minimum = value; }
+ }
+
+ ///
+ /// Gets the maximum value of current range.
+ ///
+ public double Max
+ {
+ get { return maximum; }
+ set { maximum = value; }
+ }
+
+ ///
+ /// Initializes new instance of range struct from given minimum and maximum values.
+ ///
+ /// Minimum value of the range.
+ /// Maximum value of the range.
+ public Range(double minimum, double maximum)
+ {
+ Debug.Assert(!double.IsNaN(minimum));
+ Debug.Assert(!double.IsNaN(maximum));
+
+ if (minimum < maximum)
+ {
+ this.minimum = minimum;
+ this.maximum = maximum;
+ }
+ else
+ {
+ this.minimum = maximum;
+ this.maximum = minimum;
+ }
+ }
+
+ private Range(bool isEmpty)
+ {
+ if (isEmpty)
+ {
+ this.minimum = double.PositiveInfinity;
+ this.maximum = double.NegativeInfinity;
+ }
+ else
+ {
+ minimum = 0;
+ maximum = 0;
+ }
+ }
+
+ ///
+ /// Readonly instance of empty range
+ ///
+ public static readonly Range Empty = new Range(true);
+
+ /// Returns true of this range contains no points (e.g. Min > Max).
+ public bool IsEmpty { get { return Min > Max; } }
+
+ /// Returns true of this range is a point (e.g. Min == Max).
+ public bool IsPoint { get { return Max == Min; } }
+
+ ///
+ /// Updates current instance of with minimal range which contains current range and specified value
+ ///
+ /// Value, which will be used for for current instance of range surrond
+ public void Surround(double value)
+ {
+ if (value < minimum) minimum = value;
+ if (value > maximum) maximum = value;
+ }
+
+ ///
+ /// Updates current instance of with minimal range which contains current range and specified range
+ ///
+ /// Range, which will be used for current instance of range surrond
+ public void Surround(Range range)
+ {
+ if (range.IsEmpty)
+ return;
+
+ Surround(range.minimum);
+ Surround(range.maximum);
+ }
+
+ ///
+ /// Returns a string that represents the current range.
+ ///
+ /// String that represents the current range
+ public override string ToString()
+ {
+ return "[" + minimum.ToString(CultureInfo.InvariantCulture) + "," + maximum.ToString(CultureInfo.InvariantCulture) + "]";
+ }
+
+ ///
+ /// Calculates range from current which will have the same center and which size will be larger in factor times
+ ///
+ /// Zoom factor
+ /// Zoomed with specified factor range
+ public Range Zoom(double factor)
+ {
+ if (IsEmpty)
+ return new Range(true);
+
+ double delta = (Max - Min) / 2;
+ double center = (Max + Min) / 2;
+
+ return new Range(center - delta * factor, center + delta * factor);
+ }
+
+ ///
+ /// Determines whether the specified is equal to the current range.
+ ///
+ /// The range to compare with the current .
+ /// True if the specified range is equal to the current range, false otherwise.
+ public override bool Equals(object obj)
+ {
+ Range r = (Range)obj;
+ return r.minimum == minimum && r.maximum == maximum;
+ }
+
+ ///
+ /// Returns the hash code for this instance.
+ ///
+ /// The hash code for current instance
+ public override int GetHashCode()
+ {
+ return minimum.GetHashCode() ^ maximum.GetHashCode();
+ }
+
+ ///
+ /// Returns a value that indicates whether two specified range values are equal.
+ ///
+ /// The first value to compare.
+ /// The second value to compare.
+ /// True if values are equal, false otherwise.
+ public static bool operator ==(Range first, Range second)
+ {
+ return first.Equals(second);
+ }
+
+ ///
+ /// Returns a value that indicates whether two specified ranges values are not equal.
+ ///
+ /// The first value to compare.
+ /// The second value to compare.
+ /// True if values are not equal, false otherwise.
+ public static bool operator !=(Range first, Range second)
+ {
+ return !first.Equals(second);
+ }
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Figure.cs b/InteractiveDataDisplay_OneDll/src/Figure.cs
new file mode 100644
index 000000000..2923e2d3c
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Figure.cs
@@ -0,0 +1,444 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Windows;
+using System.Windows.Media;
+using System.Linq;
+using System.ComponentModel;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Specifies the dock position of a child element that is inside a
+ ///
+ public enum Placement
+ {
+ ///
+ /// A child element that is positioned on the left side of the
+ ///
+ Left,
+
+ ///
+ /// A child element that is positioned at the top of the
+ ///
+ Top,
+
+ ///
+ /// A child element that is positioned on the right side of the
+ ///
+ Right,
+
+ ///
+ /// A child element that is positioned at the bottom of the
+ ///
+ Bottom,
+
+ ///
+ /// A child element that is positioned at the center of the
+ ///
+ Center
+ }
+
+ ///
+ /// Figure class provides special layout options that are often found in charts.
+ /// It provides attached property Placement that allows to place child elements in center, left, top, right and bottom slots.
+ /// Figure override plot-to-screen transform, so that plot coordinates are mapped to the cental part of the Figure.
+ ///
+ ///
+ /// Figure class provides two-pass algorithm that prevents well-known loop occurring on resize of figure with fixed aspect ratio: figure resize forces update of plot-to-screen transform which adjusts labels on the axes.
+ /// Change of label size may result in change of central part size which again updates plot-to-screen transform which in turn leads to axes label updates and so on.
+ ///
+ [Description("Plot with center, left, top, right and bottom slots")]
+ public class Figure : PlotBase
+ {
+ private double topHeight = 0;
+ private double topHeight2 = 0;
+ private double bottomHeight = 0;
+ private double bottomHeight2 = 0;
+ private double leftWidth = 0;
+ private double leftWidth2 = 0;
+ private double rightWidth = 0;
+ private double rightWidth2 = 0;
+
+ private Size centerSize = Size.Empty;
+
+ ///
+ /// Identify attached property
+ ///
+ public static readonly DependencyProperty PlacementProperty = DependencyProperty.RegisterAttached(
+ "Placement",
+ typeof(Placement),
+ typeof(Figure),
+ new PropertyMetadata(Placement.Center, OnPlacementPropertyChanged));
+
+ private static void OnPlacementPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
+ {
+ FrameworkElement elt = sender as FrameworkElement;
+ if (elt != null) // TODO: What if sender is not a FrameworkElement
+ {
+ UIElement parent = elt.Parent as UIElement;
+ if (parent != null)
+ parent.InvalidateMeasure();
+ }
+ }
+
+ /// Gets or sets extra padding added to effective padding that is computed from all plots in composition
+ [Category("InteractiveDataDisplay")]
+ public Thickness ExtraPadding
+ {
+ get { return (Thickness)GetValue(ExtraPaddingProperty); }
+ set { SetValue(ExtraPaddingProperty, value); }
+ }
+
+ /// Identifies dependency property
+ public static readonly DependencyProperty ExtraPaddingProperty =
+ DependencyProperty.Register("ExtraPadding", typeof(Thickness), typeof(Figure), new PropertyMetadata(new Thickness()));
+
+ ///
+ /// Computes padding with maximum values for each side from padding of all children
+ ///
+ /// Padding with maximum values for each side from padding of all children
+ protected override Thickness AggregatePadding()
+ {
+ var ep = base.AggregatePadding();
+ return new Thickness(
+ ep.Left + ExtraPadding.Left,
+ ep.Top + ExtraPadding.Top,
+ ep.Right + ExtraPadding.Right,
+ ep.Bottom + ExtraPadding.Bottom);
+ }
+
+ ///
+ /// Measures the size in layout required for child elements and determines a size for the Figure.
+ ///
+ /// The available size that this element can give to child elements. Infinity can be specified as a value to indicate that the element will size to whatever content is available.
+ /// The size that this element determines it needs during layout, based on its calculations of child element sizes.
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ // Assuming we are master panel
+ var topElts = Children.Cast().Where(elt => GetPlacement(elt) == Placement.Top);
+ var bottomElts = Children.Cast().Where(elt => GetPlacement(elt) == Placement.Bottom);
+ var centerElts = Children.Cast().Where(elt => GetPlacement(elt) == Placement.Center);
+ var rightElts = Children.Cast().Where(elt => GetPlacement(elt) == Placement.Right);
+ var leftElts = Children.Cast().Where(elt => GetPlacement(elt) == Placement.Left);
+
+ DataRect desiredRect;
+ if (IsAutoFitEnabled)
+ {
+ desiredRect = AggregateBounds();
+ if (desiredRect.IsEmpty)
+ desiredRect = new DataRect(0, 0, 1, 1);
+ SetPlotRect(desiredRect, true);
+ }
+ else // resize
+ desiredRect = PlotRect;
+
+ //First Iteration: Measuring top and bottom slots,
+ //then meassuring left and right with top and bottom output values
+
+ // Create transform for first iteration
+ if (double.IsNaN(availableSize.Width) || double.IsNaN(availableSize.Height) ||
+ double.IsInfinity(availableSize.Width) || double.IsInfinity(availableSize.Height))
+ availableSize = new Size(100, 100);
+
+ Fit(desiredRect, availableSize);
+
+ // Measure top and bottom slots
+ double topBottomWidth = 0;
+ double topBottomHeight = 0;
+ topHeight = 0;
+ bottomHeight = 0;
+
+ foreach (var elt in topElts)
+ {
+ elt.Measure(availableSize);
+ var ds = elt.DesiredSize;
+ topBottomWidth = Math.Max(topBottomWidth, ds.Width);
+ topHeight += ds.Height;
+ }
+ topBottomHeight += topHeight;
+
+ foreach (var elt in bottomElts)
+ {
+ elt.Measure(availableSize);
+ var ds = elt.DesiredSize;
+ topBottomWidth = Math.Max(topBottomWidth, ds.Width);
+ bottomHeight += ds.Height;
+ }
+ topBottomHeight += bottomHeight;
+
+ // Measure left and right slots
+ double leftRightWidth = 0;
+ double leftRightHeight = 0;
+ leftWidth = 0;
+ rightWidth = 0;
+
+ foreach (var elt in leftElts)
+ {
+
+ elt.Measure(availableSize);
+ var ds = elt.DesiredSize;
+ leftRightHeight = Math.Max(leftRightHeight, ds.Height);
+ leftWidth += ds.Width;
+ }
+ leftRightWidth += leftWidth;
+
+ foreach (var elt in rightElts)
+ {
+ elt.Measure(availableSize);
+ var ds = elt.DesiredSize;
+ leftRightHeight = Math.Max(leftRightHeight, ds.Height);
+ rightWidth += ds.Width;
+ }
+ leftRightWidth += rightWidth;
+
+ //Measure center elements
+ Size availCenterSize = new Size(Math.Max(0, availableSize.Width - leftRightWidth), Math.Max(0, availableSize.Height - topBottomHeight));
+ Fit(desiredRect, availCenterSize);
+
+ foreach (var elt in centerElts)
+ {
+ elt.Measure(availCenterSize);
+ }
+
+ // Remeasure top and bottom slots
+ double topBottomWidth2 = 0;
+ double topBottomHeight2 = 0;
+ topHeight2 = 0;
+ bottomHeight2 = 0;
+
+ foreach (var elt in topElts)
+ {
+ elt.Measure(new Size(availCenterSize.Width, elt.DesiredSize.Height));
+ var ds = elt.DesiredSize;
+ topBottomWidth2 = Math.Max(topBottomWidth2, ds.Width);
+ topHeight2 += ds.Height;
+ }
+ topBottomHeight2 += topHeight2;
+
+ foreach (var elt in bottomElts)
+ {
+ elt.Measure(new Size(availCenterSize.Width, elt.DesiredSize.Height));
+ var ds = elt.DesiredSize;
+ topBottomWidth2 = Math.Max(topBottomWidth2, ds.Width);
+ bottomHeight2 += ds.Height;
+ }
+ topBottomHeight2 += bottomHeight2;
+
+
+ //Scaling elements of their new meassured height it not equal to first
+ if (bottomHeight2 > bottomHeight)
+ {
+ ScaleTransform transform = new ScaleTransform { ScaleY = bottomHeight / bottomHeight2 };
+ foreach (var elt in bottomElts)
+ {
+ elt.RenderTransform = transform;
+ }
+ }
+
+ if (topHeight2 > topHeight)
+ {
+ ScaleTransform transform = new ScaleTransform { ScaleY = topHeight / topHeight2 };
+ foreach (var elt in topElts)
+ {
+ elt.RenderTransform = transform;
+ }
+ }
+
+ // ReMeasure left and right slots
+ double leftRightWidth2 = 0;
+ double leftRightHeight2 = 0;
+ leftWidth2 = 0;
+ rightWidth2 = 0;
+
+ foreach (var elt in leftElts)
+ {
+ elt.Measure(new Size(elt.DesiredSize.Width, availCenterSize.Height));
+ var ds = elt.DesiredSize;
+ leftRightHeight2 = Math.Max(leftRightHeight2, ds.Height);
+ leftWidth2 += ds.Width;
+ }
+ leftRightWidth2 += leftWidth2;
+
+ foreach (var elt in rightElts)
+ {
+ elt.Measure(new Size(elt.DesiredSize.Width, availCenterSize.Height));
+ var ds = elt.DesiredSize;
+ leftRightHeight2 = Math.Max(leftRightHeight2, ds.Height);
+ rightWidth2 += ds.Width;
+ }
+ leftRightWidth2 += rightWidth2;
+
+ //Scaling elements of their new meassured height it not equal to first
+ if (leftWidth2 > leftWidth)
+ {
+ ScaleTransform transform = new ScaleTransform { ScaleX = leftWidth / leftWidth2 };
+ foreach (var elt in leftElts)
+ {
+ elt.RenderTransform = transform;
+ }
+ }
+
+ if (rightWidth2 > rightWidth)
+ {
+ ScaleTransform transform = new ScaleTransform { ScaleX = rightWidth / rightWidth2 };
+ foreach (var elt in rightElts)
+ {
+ elt.RenderTransform = transform;
+ }
+ }
+
+ centerSize = availCenterSize;
+ return new Size(availCenterSize.Width + leftRightWidth, availCenterSize.Height + topBottomHeight);
+ }
+
+ ///
+ /// Positions child elements and determines a size for a Figure
+ ///
+ /// The final area within the parent that Figure should use to arrange itself and its children
+ /// The actual size used
+ protected override Size ArrangeOverride(Size finalSize)
+ {
+ var topElts = Children.Cast().Where(elt => GetPlacement(elt) == Placement.Top).ToArray();
+ var bottomElts = Children.Cast().Where(elt => GetPlacement(elt) == Placement.Bottom).ToArray();
+ var centerElts = Children.Cast().Where(elt => GetPlacement(elt) == Placement.Center);
+ var rightElts = Children.Cast().Where(elt => GetPlacement(elt) == Placement.Right).ToArray();
+ var leftElts = Children.Cast().Where(elt => GetPlacement(elt) == Placement.Left).ToArray();
+
+ double x = 0, y = 0;
+
+ //Arranging top elements and setting clip bounds
+ if (topHeight < topHeight2)
+ {
+ foreach (var elt in topElts)
+ {
+ double finalHeight = elt.DesiredSize.Height * topHeight / topHeight2;
+ elt.Arrange(new Rect(leftWidth, y, centerSize.Width, finalHeight));
+ elt.Clip = new RectangleGeometry { Rect = new Rect(-leftWidth, 0, finalSize.Width, finalHeight) };
+ y += finalHeight;
+ }
+ }
+ else
+ {
+ double iy = topHeight;
+ for (int i = topElts.Length - 1; i > -1; i--)
+ {
+ UIElement elt = topElts[i];
+ elt.Arrange(new Rect(leftWidth, iy - elt.DesiredSize.Height, centerSize.Width, elt.DesiredSize.Height));
+ elt.Clip = new RectangleGeometry { Rect = new Rect(-leftWidth, 0, finalSize.Width, elt.DesiredSize.Height) };
+ iy -= elt.DesiredSize.Height;
+ }
+ y = topHeight;
+ }
+
+ // Arranging left elements and setting clip bounds
+ if (leftWidth < leftWidth2)
+ {
+ foreach (var elt in leftElts)
+ {
+ double finalWidth = elt.DesiredSize.Width * leftWidth / leftWidth2;
+ elt.Arrange(new Rect(x, topHeight, finalWidth, centerSize.Height));
+ elt.Clip = new RectangleGeometry { Rect = new Rect(0, -topHeight, finalWidth, finalSize.Height) };
+ x += finalWidth;
+ }
+ }
+ else
+ {
+ double ix = leftWidth;
+ for (int i = leftElts.Length - 1; i > -1; i--)
+ {
+ UIElement elt = leftElts[i];
+ elt.Arrange(new Rect(ix - elt.DesiredSize.Width, topHeight, elt.DesiredSize.Width, centerSize.Height));
+ elt.Clip = new RectangleGeometry { Rect = new Rect(0, -topHeight, elt.DesiredSize.Width, finalSize.Height) };
+ ix -= elt.DesiredSize.Width;
+ }
+ x = leftWidth;
+ }
+
+ // Arranging center elements
+ foreach (var elt in centerElts)
+ {
+ elt.Arrange(new Rect(leftWidth, topHeight, centerSize.Width, centerSize.Height));
+ }
+
+ x += centerSize.Width;
+ y += centerSize.Height;
+
+ // Arranging bottom elements and setting clip bounds
+ if (bottomHeight < bottomHeight2)
+ {
+ foreach (var elt in bottomElts)
+ {
+ double finalHeight = elt.DesiredSize.Height * bottomHeight / bottomHeight2;
+ elt.Arrange(new Rect(leftWidth, y, centerSize.Width, finalHeight));
+ elt.Clip = new RectangleGeometry { Rect = new Rect(-leftWidth, 0, finalSize.Width, finalHeight) };
+ y += finalHeight;
+ }
+ }
+ else
+ {
+ for (int i = bottomElts.Length - 1; i > -1; i--)
+ {
+ UIElement elt = bottomElts[i];
+ elt.Arrange(new Rect(leftWidth, y, centerSize.Width, elt.DesiredSize.Height));
+ elt.Clip = new RectangleGeometry { Rect = new Rect(-leftWidth, 0, finalSize.Width, elt.DesiredSize.Height) };
+ y += elt.DesiredSize.Height;
+ }
+ }
+
+ // Arranging right elements and setting clip bounds
+ if (rightWidth < rightWidth2)
+ {
+ foreach (var elt in rightElts)
+ {
+ double finalWidth = elt.DesiredSize.Width * rightWidth / rightWidth2;
+ elt.Arrange(new Rect(x, topHeight, finalWidth, centerSize.Height));
+ elt.Clip = new RectangleGeometry { Rect = new Rect(0, -topHeight, finalWidth, finalSize.Height) };
+ x += finalWidth;
+ }
+ }
+ else
+ {
+ for (int i = rightElts.Length - 1; i > -1; i--)
+ {
+ UIElement elt = rightElts[i];
+ elt.Arrange(new Rect(x, topHeight, elt.DesiredSize.Width, centerSize.Height));
+ elt.Clip = new RectangleGeometry { Rect = new Rect(0, -topHeight, elt.DesiredSize.Width, finalSize.Height) };
+ x += elt.DesiredSize.Width;
+ }
+ }
+
+ return new Size(x, y);
+ }
+
+ ///
+ /// Sets the value of the attached property to a specified element.
+ ///
+ /// The element to which the attached property is written.
+ /// The needed value.
+ public static void SetPlacement(DependencyObject element, Placement placement)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+
+ element.SetValue(PlacementProperty, placement);
+ }
+
+ ///
+ /// Gets the value of the attached property to a specified element.
+ ///
+ /// The element from which the property value is read.
+ /// The property value for the element.
+ /// The default value of FigurePlacement property is Placement.Center
+ public static Placement GetPlacement(DependencyObject element)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+
+ return (Placement)element.GetValue(PlacementProperty);
+ }
+ }
+
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/InteractiveDataDisplay.WPF.csproj b/InteractiveDataDisplay_OneDll/src/InteractiveDataDisplay.WPF.csproj
new file mode 100644
index 000000000..43e9c6c9c
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/InteractiveDataDisplay.WPF.csproj
@@ -0,0 +1,72 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {2D34544C-2DF2-4B20-A43A-6C8D2DF3DD82}
+ Library
+ Properties
+ InteractiveDataDisplay.WPF
+ InteractiveDataDisplay.WPF
+ net452;net46
+ 512
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ TRACE;DEBUG;NETFRAMEWORK;NETFRAMEWORK;
+ prompt
+ 4
+ true
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE;RELEASE;NETFRAMEWORK;;RELEASE;NETFRAMEWORK;RELEASE;NETFRAMEWORK;
+ prompt
+ 4
+ true
+
+
+ $(MSBuildExtensionsPath)\$(VisualStudioVersion)\Bin\Microsoft.CSharp.targets
+ 1.1.0
+ Microsoft; MSU ITIS Lab
+ Sergey Berezin, Vassily Lyutsarev, Nikita Skoblov, Natalia Stepanova
+ Interactive Data Display for WPF is a set of controls for adding interactive visualization of dynamic data to your application.
+ Copyright 2017 Microsoft Corporation
+ idd wpf visualization plot plots plotting chart charting data interactive datavisualization
+ https://github.com/Microsoft/InteractiveDataDisplay.WPF
+ https://github.com/Microsoft/InteractiveDataDisplay.WPF/blob/master/LICENSE
+ true
+ false
+ sn.key.snk
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Designer
+ MSBuild:Compile
+
+
+
+
\ No newline at end of file
diff --git a/InteractiveDataDisplay_OneDll/src/Legend/CountToVisibilityConverter.cs b/InteractiveDataDisplay_OneDll/src/Legend/CountToVisibilityConverter.cs
new file mode 100644
index 000000000..5763b2342
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Legend/CountToVisibilityConverter.cs
@@ -0,0 +1,46 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System.Windows.Data;
+using System;
+using System.Windows;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Converts the count of collection to Visibility.
+ ///
+ public class CountToVisibilityConverter : IValueConverter
+ {
+ #region IValueConverter Members
+
+ ///
+ /// Converts any numeric value to Visibility.
+ ///
+ /// A value of any numeric type.
+ ///
+ ///
+ ///
+ /// Visible if the value is positive. Collapsed if the value is negative or 0.
+ public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+ {
+ return (int)value > 0 ? Visibility.Visible : Visibility.Collapsed;
+ }
+
+ ///
+ /// Converts Visibility to integer value.
+ ///
+ /// A value of Visibility enum.
+ ///
+ ///
+ ///
+ /// 1 if the value is Visible. 0 if the value is Collapsed.
+ public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+ {
+ return (Visibility)value == Visibility.Visible ? 1 : 0;
+ }
+
+ #endregion
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Legend/DefaultTemplates.xaml b/InteractiveDataDisplay_OneDll/src/Legend/DefaultTemplates.xaml
new file mode 100644
index 000000000..decc97796
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Legend/DefaultTemplates.xaml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/InteractiveDataDisplay_OneDll/src/Legend/Legend.cs b/InteractiveDataDisplay_OneDll/src/Legend/Legend.cs
new file mode 100644
index 000000000..5a6c8498e
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Legend/Legend.cs
@@ -0,0 +1,116 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.ComponentModel;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Control for placing legend contents.
+ ///
+ [Description("Control for legend contents")]
+ public class Legend : ContentControl
+ {
+ ///
+ /// Gets or sets the number of items in legend. Value of this property
+ /// is automatically updated set to item count if content is .
+ /// It is recommended to use this property as read only.
+ ///
+ [Browsable(false)]
+ public int ItemsCount
+ {
+ get { return (int)GetValue(ItemsCountProperty); }
+ private set { SetValue(ItemsCountProperty, value); }
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty ItemsCountProperty =
+ DependencyProperty.Register("ItemsCount", typeof(int), typeof(Legend), new PropertyMetadata(0));
+
+ ///
+ /// This method is called when the content property changes.
+ ///
+ /// Old value of property.
+ /// New value of property.
+ protected override void OnContentChanged(object oldContent, object newContent)
+ {
+ var panel = oldContent as Panel;
+ if (panel != null)
+ panel.LayoutUpdated -= OnContentLayoutUpdated;
+ panel = newContent as Panel;
+ if (panel != null)
+ panel.LayoutUpdated += OnContentLayoutUpdated;
+ UpdateItemsCount();
+ base.OnContentChanged(oldContent, newContent);
+ }
+
+ private void OnContentLayoutUpdated(object sender, EventArgs e)
+ {
+ UpdateItemsCount();
+ }
+
+ ///
+ /// Initializes a new instance of class.
+ ///
+ public Legend()
+ {
+ DefaultStyleKey = typeof(Legend);
+ IsTabStop = false;
+ }
+
+ ///
+ /// This method is invoked whenever application code or internal processes call ApplyTemplate.
+ ///
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+ UpdateItemsCount();
+ }
+
+ private void UpdateItemsCount()
+ {
+ var panel = Content as Panel;
+ if (panel != null)
+ ItemsCount = panel.Children.Count;
+ else
+ ItemsCount = Content != null ? 1 : 0;
+ }
+
+ ///
+ /// Returns value of attached property that controls
+ /// if object is visible in legend. Default value is true.
+ ///
+ /// An object with a legend.
+ /// Visibility of specified object in legend.
+ public static bool GetIsVisible(DependencyObject obj)
+ {
+ if (obj != null)
+ return (bool)obj.GetValue(IsVisibleProperty);
+ else
+ return true;
+ }
+
+ ///
+ /// Sets attached property. Objects with false value
+ /// of this property are not shown in legend.
+ /// An object to be presented in a legend.
+ /// Legend visibility flag
+ public static void SetIsVisible(DependencyObject obj, bool isVisible)
+ {
+ if (obj != null)
+ obj.SetValue(IsVisibleProperty, isVisible);
+ }
+
+ ///
+ /// Identifies attached property to get or set visibility of a legend.
+ ///
+ public static readonly DependencyProperty IsVisibleProperty =
+ DependencyProperty.RegisterAttached("IsVisible", typeof(bool), typeof(Legend), new PropertyMetadata(true));
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Legend/LegendItemsPanel.cs b/InteractiveDataDisplay_OneDll/src/Legend/LegendItemsPanel.cs
new file mode 100644
index 000000000..e5057fb4d
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Legend/LegendItemsPanel.cs
@@ -0,0 +1,184 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Windows.Controls;
+using System.Windows;
+using System.ComponentModel;
+
+namespace InteractiveDataDisplay.WPF
+{
+ /// This panel automatically builds legend for given
+ [Description("Presents legend items for a plot")]
+ public class LegendItemsPanel : Panel
+ {
+ private IDisposable unsubsrciber;
+ private PlotBase masterPlot = null;
+
+ ///
+ /// Gets of sets plot to build legend for
+ ///
+ [Category("InteractiveDataDisplay")]
+ [Description("Plot to build legend for")]
+ public PlotBase MasterPlot
+ {
+ get { return (PlotBase)GetValue(MasterPlotProperty); }
+ set { SetValue(MasterPlotProperty, value); }
+ }
+
+ ///
+ /// Identifies property
+ ///
+ public static readonly DependencyProperty MasterPlotProperty =
+ DependencyProperty.Register("MasterPlot", typeof(PlotBase), typeof(LegendItemsPanel), new PropertyMetadata(null,
+ (o, e) =>
+ {
+ LegendItemsPanel panel = (LegendItemsPanel)o;
+ if (panel != null)
+ {
+ panel.OnMasterPlotChanged(e);
+ }
+ }));
+
+ private void LegendItemsPanelUnloaded(object sender, RoutedEventArgs e)
+ {
+ if (MasterPlot == null)
+ masterPlot = null;
+ }
+
+ private void LegendItemsPanelLoaded(object sender, RoutedEventArgs e)
+ {
+ if (MasterPlot == null)
+ masterPlot = PlotBase.FindMaster(this);
+ else
+ masterPlot = MasterPlot;
+
+ Resubscribe();
+
+ }
+
+ private void OnMasterPlotChanged(DependencyPropertyChangedEventArgs e)
+ {
+ PlotBase newPlot = (PlotBase)e.NewValue;
+
+ if (newPlot != null)
+ masterPlot = newPlot;
+
+ Resubscribe();
+ }
+
+ private void Resubscribe()
+ {
+ if (unsubsrciber != null)
+ {
+ unsubsrciber.Dispose();
+ unsubsrciber = null;
+ }
+
+ if (masterPlot != null)
+ {
+ //unsubsrciber = masterPlot.CompositionChange.Subscribe(change => RefreshContents());
+ RefreshContents();
+ }
+ }
+
+ ///
+ /// Measures the size in layout required for child elements and determines a size for parent.
+ ///
+ /// The available size that this element can give to child elements. Infinity can be specified as a value to indicate that the element will size to whatever content is available.
+ /// The size that this element determines it needs during layout, based on its calculations of child element sizes.
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ Size result = new Size();
+ foreach (UIElement c in Children)
+ {
+ c.Measure(availableSize);
+ result.Width = Math.Max(result.Width, c.DesiredSize.Width);
+ if (!Double.IsInfinity(availableSize.Height))
+ availableSize.Height = Math.Max(0, availableSize.Height - c.DesiredSize.Height);
+ result.Height += c.DesiredSize.Height;
+ }
+ return result;
+ }
+
+ ///
+ /// Positions child elements and determines a size for parent.
+ ///
+ /// The final area within the parent that Figure should use to arrange itself and its children.
+ /// The actual size used.
+ protected override Size ArrangeOverride(Size finalSize)
+ {
+ double y = 0;
+ foreach (UIElement c in Children)
+ {
+ c.Arrange(new Rect(new Point(0, y), c.DesiredSize));
+ y += c.DesiredSize.Height;
+ }
+ return finalSize;
+ }
+
+ ///
+ /// Initializes a new instance of class.
+ ///
+ public LegendItemsPanel()
+ {
+ var rd = new ResourceDictionary();
+ rd = (ResourceDictionary)Application.LoadComponent(new System.Uri("/InteractiveDataDisplay.WPF;component/Legend/DefaultTemplates.xaml", System.UriKind.Relative));
+ Resources.MergedDictionaries.Add(rd);
+
+ Loaded += LegendItemsPanelLoaded;
+ Unloaded += LegendItemsPanelUnloaded;
+ }
+
+ ///
+ /// Constructs legend UI for current plot
+ ///
+ protected virtual void RefreshContents()
+ {
+ Children.Clear();
+
+ if (masterPlot != null)
+ {
+ foreach (var elt in masterPlot.RelatedPlots)
+ {
+ var c = CreateLegendContent(elt);
+ if (c != null)
+ Children.Add(c);
+ }
+ }
+
+ InvalidateMeasure();
+ }
+
+ ///
+ /// Creates legend content from given UIElement
+ ///
+ /// UIElement for legend content construction
+ /// UIElement which reperesents input UIElement in legend control
+ protected virtual UIElement CreateLegendContent(UIElement element)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+
+ if (!Legend.GetIsVisible(element))
+ return null;
+
+ Type eltype = element.GetType();
+ while (eltype != null)
+ {
+ var template = Resources[eltype.FullName] as DataTemplate;
+ if (template != null)
+ {
+ var newElt = template.LoadContent() as FrameworkElement;
+ if (newElt != null)
+ newElt.DataContext = element;
+
+ return newElt;
+ }
+ eltype = eltype.BaseType;
+ }
+ return null;
+ }
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Legend/SizeLegendControl.cs b/InteractiveDataDisplay_OneDll/src/Legend/SizeLegendControl.cs
new file mode 100644
index 000000000..f1ee4c74f
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Legend/SizeLegendControl.cs
@@ -0,0 +1,300 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Shapes;
+using System.ComponentModel;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// A control to show information about size of drawing markers in a legend.
+ ///
+ [Description("Visually maps value to screen units")]
+ public class SizeLegendControl : ContentControl
+ {
+ #region Fields
+
+ private Canvas image;
+ private Axis axis;
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets or sets the range of axis values.
+ /// Default value is [0, 1].
+ ///
+ [Category("InteractiveDataDisplay")]
+ [Description("Range of mapped values")]
+ public Range Range
+ {
+ get { return (Range)GetValue(RangeProperty); }
+ set { SetValue(RangeProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the filling color.
+ /// Default value is black color.
+ ///
+ [Category("Appearance")]
+ public Color Color
+ {
+ get { return (Color)GetValue(ColorProperty); }
+ set { SetValue(ColorProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the maximum value of displaying height.
+ /// Default value is 20.
+ ///
+ [Category("InteractiveDataDisplay")]
+ [Description("Maximum screen height of palette")]
+ public new double MaxHeight
+ {
+ get { return (double)GetValue(MaxHeightProperty); }
+ set { SetValue(MaxHeightProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the value indicating whether an axis should be rendered or not.
+ /// Default value is true.
+ ///
+ [Category("InteractiveDataDisplay")]
+ public bool IsAxisVisible
+ {
+ get { return (bool)GetValue(IsAxisVisibleProperty); }
+ set { SetValue(IsAxisVisibleProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the minimum of size to display in a legend. This is screen size that corresponds to minimum value of data.
+ /// Default value is Double.NaN.
+ ///
+ [Category("InteractiveDataDisplay")]
+ [Description("Screen size that corresponds to minimum value")]
+ public double Min
+ {
+ get { return (double)GetValue(MinProperty); }
+ set { SetValue(MinProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the maximum of size to display in a legend. This is screen size that corresponds to maximum value of data.
+ /// Default value is Double.NaN.
+ ///
+ [Category("InteractiveDataDisplay")]
+ [Description("Screen size that corresponds to maximum value")]
+ public double Max
+ {
+ get { return (double)GetValue(MaxProperty); }
+ set { SetValue(MaxProperty, value); }
+ }
+
+ #endregion
+
+ #region Dependency properties
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty RangeProperty = DependencyProperty.Register(
+ "Range",
+ typeof(Range),
+ typeof(SizeLegendControl),
+ new PropertyMetadata(new Range(0, 1), OnRangeChanged));
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty ColorProperty = DependencyProperty.Register(
+ "Color",
+ typeof(Color),
+ typeof(SizeLegendControl),
+ new PropertyMetadata(Colors.Black, OnColorChanged));
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static new readonly DependencyProperty MaxHeightProperty = DependencyProperty.Register(
+ "MaxHeight",
+ typeof(double),
+ typeof(SizeLegendControl),
+ new PropertyMetadata(20.0, OnMinMaxChanged));
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty IsAxisVisibleProperty = DependencyProperty.Register(
+ "IsAxisVisible",
+ typeof(bool),
+ typeof(SizeLegendControl),
+ new PropertyMetadata(true));
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty MinProperty = DependencyProperty.Register(
+ "Min",
+ typeof(double),
+ typeof(SizeLegendControl),
+ new PropertyMetadata(Double.NaN, OnMinMaxChanged));
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty MaxProperty = DependencyProperty.Register(
+ "Max",
+ typeof(double),
+ typeof(SizeLegendControl),
+ new PropertyMetadata(Double.NaN, OnMinMaxChanged));
+
+ #endregion
+
+ #region ctor
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public SizeLegendControl()
+ {
+ StackPanel stackPanel = new StackPanel();
+
+ image = new Canvas { HorizontalAlignment = HorizontalAlignment.Stretch };
+ axis = new Axis { AxisOrientation = AxisOrientation.Bottom, HorizontalAlignment = HorizontalAlignment.Stretch };
+
+ stackPanel.Children.Add(image);
+ stackPanel.Children.Add(axis);
+
+ Content = stackPanel;
+
+ SizeChanged += (o, e) =>
+ {
+ if (e.PreviousSize.Width == 0 || e.PreviousSize.Height == 0 || Double.IsNaN(e.PreviousSize.Width) || Double.IsNaN(e.PreviousSize.Height))
+ UpdateCanvas();
+ };
+
+ IsTabStop = false;
+ }
+
+ #endregion
+
+ #region Private methods
+
+ private void UpdateCanvas()
+ {
+ if (Width == 0 || Double.IsNaN(Width))
+ return;
+
+ image.Children.Clear();
+
+ double maxHeight = Double.IsNaN(Max) ? Range.Max : Max;
+ double minHeight = Double.IsNaN(Min) ? Range.Min : Min;
+
+ double visHeight = Math.Min(maxHeight, MaxHeight);
+
+ image.Width = Width;
+ axis.Width = Width;
+ image.Height = visHeight;
+
+ PathFigure pathFigure = new PathFigure();
+ pathFigure.StartPoint = new Point(0, maxHeight);
+
+ LineSegment lineSegment1 = new LineSegment();
+ lineSegment1.Point = new Point(Width, maxHeight);
+ LineSegment lineSegment2 = new LineSegment();
+ lineSegment2.Point = new Point(Width, 0);
+ LineSegment lineSegment3 = new LineSegment();
+ lineSegment3.Point = new Point(0, maxHeight - minHeight);
+ LineSegment lineSegment4 = new LineSegment();
+ lineSegment4.Point = new Point(0, maxHeight);
+
+ PathSegmentCollection pathSegmentCollection = new PathSegmentCollection();
+ pathSegmentCollection.Add(lineSegment1);
+ pathSegmentCollection.Add(lineSegment2);
+ pathSegmentCollection.Add(lineSegment3);
+ pathSegmentCollection.Add(lineSegment4);
+
+ pathFigure.Segments = pathSegmentCollection;
+ PathFigureCollection pathFigureCollection = new PathFigureCollection();
+ pathFigureCollection.Add(pathFigure);
+ PathGeometry pathGeometry = new PathGeometry();
+ pathGeometry.Figures = pathFigureCollection;
+
+ Path path = new Path();
+ // TODO: Make it dependency property of SizeControl
+ path.Fill = new SolidColorBrush(Colors.LightGray);
+
+ path.Data = pathGeometry;
+ path.Stroke = new SolidColorBrush(Color);
+
+ if (!Double.IsNaN(MaxHeight))
+ {
+ RectangleGeometry clip = new RectangleGeometry();
+ clip.Rect = new Rect(0, maxHeight - this.MaxHeight, Width, this.MaxHeight);
+ path.Clip = clip;
+ }
+
+ Canvas.SetTop(path, image.Height - maxHeight);
+ image.Children.Add(path);
+
+ if (visHeight < maxHeight)
+ {
+ Line top;
+ if (minHeight >= visHeight)
+ top = new Line { X1 = 0, Y1 = 0, X2 = Width, Y2 = 0 };
+ else
+ top = new Line
+ {
+ X1 = Width * (visHeight - minHeight) / (maxHeight - minHeight),
+ Y1 = 0,
+ X2 = Width,
+ Y2 = 0
+ };
+ top.Stroke = new SolidColorBrush(Colors.Black);
+ top.StrokeDashArray = new DoubleCollection();
+ top.StrokeDashArray.Add(5);
+ top.StrokeDashArray.Add(5);
+ image.Children.Add(top);
+ }
+ }
+
+ private static void OnRangeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ SizeLegendControl control = (SizeLegendControl)d;
+ control.OnRangeChanged();
+ }
+
+ private void OnRangeChanged()
+ {
+ axis.Range = Range;
+ UpdateCanvas();
+ }
+
+ private static void OnColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ SizeLegendControl control = (SizeLegendControl)d;
+ control.OnColorChanged();
+ }
+
+ private void OnColorChanged()
+ {
+ if (image.Children.Count > 0)
+ ((Path)image.Children[0]).Fill = new SolidColorBrush(Color);
+ }
+
+ private static void OnMinMaxChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ SizeLegendControl control = (SizeLegendControl)d;
+ control.UpdateCanvas();
+ }
+
+ #endregion
+
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Navigation/KeyboardNavigation.cs b/InteractiveDataDisplay_OneDll/src/Navigation/KeyboardNavigation.cs
new file mode 100644
index 000000000..f48e05423
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Navigation/KeyboardNavigation.cs
@@ -0,0 +1,195 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.ComponentModel;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Provides keyboard navigation around viewport of parent .
+ ///
+ ///
+ /// Place instance of inside your element to enable keyboard navigation over it.
+ ///
+ [Description("Navigates parent plot by keyboard commands")]
+ public class KeyboardNavigation : Control
+ {
+ private PlotBase masterPlot = null;
+
+ ///
+ /// Initializes a new instance of class.
+ ///
+ public KeyboardNavigation()
+ {
+ Loaded += KeyboardNavigationLoaded;
+ Unloaded += KeyboardNavigationUnloaded;
+
+ KeyDown += KeyboardNavigationKeyDown;
+ KeyUp += KeyboardNavigationKeyUp;
+ }
+
+ /// Gets or sets vertical navigation status. True means that user can navigate along Y axis
+ /// The default value is true
+ [Category("InteractiveDataDisplay")]
+ public bool IsVerticalNavigationEnabled
+ {
+ get { return (bool)GetValue(IsVerticalNavigationEnabledProperty); }
+ set { SetValue(IsVerticalNavigationEnabledProperty, value); }
+ }
+
+ /// Identifies property
+ public static readonly DependencyProperty IsVerticalNavigationEnabledProperty =
+ DependencyProperty.Register("IsVerticalNavigationEnabled", typeof(bool), typeof(KeyboardNavigation), new PropertyMetadata(true));
+
+ /// Gets or sets horizontal navigation status. True means that user can navigate along X axis
+ /// The default value is true
+ [Category("InteractiveDataDisplay")]
+ public bool IsHorizontalNavigationEnabled
+ {
+ get { return (bool)GetValue(IsHorizontalNavigationEnabledProperty); }
+ set { SetValue(IsHorizontalNavigationEnabledProperty, value); }
+ }
+
+ /// Identifies property
+ public static readonly DependencyProperty IsHorizontalNavigationEnabledProperty =
+ DependencyProperty.Register("IsHorizontalNavigationEnabled", typeof(bool), typeof(KeyboardNavigation), new PropertyMetadata(true));
+
+ void KeyboardNavigationUnloaded(object sender, RoutedEventArgs e)
+ {
+ masterPlot = null;
+ }
+
+ void KeyboardNavigationLoaded(object sender, RoutedEventArgs e)
+ {
+ masterPlot = PlotBase.FindMaster(this);
+ }
+
+ private void KeyboardNavigationKeyDown(object sender, KeyEventArgs e)
+ {
+ if (masterPlot != null)
+ {
+
+ if (e.Key == Key.Up && IsVerticalNavigationEnabled)
+ {
+ var rect = masterPlot.PlotRect;
+ double dy = rect.Height / 200;
+
+ masterPlot.SetPlotRect(new DataRect(
+ rect.XMin,
+ rect.YMin - dy,
+ rect.XMin + rect.Width,
+ rect.YMin - dy + rect.Height));
+
+ masterPlot.IsAutoFitEnabled = false;
+ e.Handled = true;
+ }
+ if (e.Key == Key.Down && IsVerticalNavigationEnabled)
+ {
+ var rect = masterPlot.PlotRect;
+ double dy = - rect.Height / 200;
+
+ masterPlot.SetPlotRect(new DataRect(
+ rect.XMin,
+ rect.YMin - dy,
+ rect.XMin + rect.Width,
+ rect.YMin - dy + rect.Height));
+
+ masterPlot.IsAutoFitEnabled = false;
+ e.Handled = true;
+ }
+ if (e.Key == Key.Right && IsHorizontalNavigationEnabled)
+ {
+ var rect = masterPlot.PlotRect;
+ double dx = - rect.Width / 200;
+
+ masterPlot.SetPlotRect(new DataRect(
+ rect.XMin + dx,
+ rect.YMin,
+ rect.XMin + dx + rect.Width,
+ rect.YMin + rect.Height));
+
+ masterPlot.IsAutoFitEnabled = false;
+ e.Handled = true;
+ }
+ if (e.Key == Key.Left && IsHorizontalNavigationEnabled)
+ {
+ var rect = masterPlot.PlotRect;
+ double dx = rect.Width / 200;
+
+ masterPlot.SetPlotRect(new DataRect(
+ rect.XMin + dx,
+ rect.YMin,
+ rect.XMin + dx + rect.Width,
+ rect.YMin + rect.Height));
+
+ masterPlot.IsAutoFitEnabled = false;
+ e.Handled = true;
+ }
+ if (e.Key == Key.Subtract)
+ {
+ DoZoom(1.2);
+ masterPlot.IsAutoFitEnabled = false;
+ e.Handled = true;
+ }
+ if (e.Key == Key.Add)
+ {
+ DoZoom(1 / 1.2);
+ masterPlot.IsAutoFitEnabled = false;
+ e.Handled = true;
+ }
+ if (e.Key == Key.Home)
+ {
+ masterPlot.IsAutoFitEnabled = true;
+ e.Handled = true;
+ }
+ }
+ }
+
+ private void DoZoom(double factor)
+ {
+ if (masterPlot != null)
+ {
+ var rect = masterPlot.PlotRect;
+
+ if (IsHorizontalNavigationEnabled)
+ rect.X = rect.X.Zoom(factor);
+ if (IsVerticalNavigationEnabled)
+ rect.Y = rect.Y.Zoom(factor);
+
+ if (IsZoomEnable(rect))
+ {
+ masterPlot.SetPlotRect(rect);
+ masterPlot.IsAutoFitEnabled = false;
+ }
+ }
+ }
+
+ private bool IsZoomEnable(DataRect rect)
+ {
+ bool res = true;
+ if (IsHorizontalNavigationEnabled)
+ {
+ double e_max = Math.Log(Math.Max(Math.Abs(rect.XMax), Math.Abs(rect.XMin)), 2);
+ double log = Math.Log(rect.Width, 2);
+ res = log > e_max - 40;
+ }
+ if (IsVerticalNavigationEnabled)
+ {
+ double e_max = Math.Log(Math.Max(Math.Abs(rect.YMax), Math.Abs(rect.YMin)), 2);
+ double log = Math.Log(rect.Height, 2);
+ res = res && log > e_max - 40;
+ }
+ return res;
+ }
+
+ void KeyboardNavigationKeyUp(object sender, KeyEventArgs e)
+ {
+ }
+ }
+}
+
+
diff --git a/InteractiveDataDisplay_OneDll/src/Navigation/MouseNavigation.cs b/InteractiveDataDisplay_OneDll/src/Navigation/MouseNavigation.cs
new file mode 100644
index 000000000..f2e284e59
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Navigation/MouseNavigation.cs
@@ -0,0 +1,499 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Shapes;
+using System.ComponentModel;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Provides mouse navigation around viewport of parent .
+ ///
+ ///
+ /// Place instance of inside your element to enable mouse navigation over it.
+ ///
+ [Description("Navigates parent plot by mouse")]
+ public class MouseNavigation : Panel
+ {
+ private Canvas navigationCanvas = new Canvas
+ {
+ // This background brush allows Canvas to intercept mouse events while remaining transparent
+ Background = new SolidColorBrush(Color.FromArgb(0, 255, 255, 255))
+ };
+
+ private PlotBase masterPlot = null;
+
+ /// First parent Control in visual tree. Used to receive focus on mouse click
+ private Control parentControl = null;
+
+ private bool isPanning = false;
+ private bool isSelecting = false;
+ private bool wasInside = false;
+ private bool isLeftClicked = false;
+
+ private bool selectingStarted = false;
+ private Point panningStart;
+ private Point panningEnd;
+ private Point selectStart;
+ private Point selectEnd;
+ private DateTime lastClick;
+
+ private bool selectingMode = true;
+
+ private Rectangle selectArea;
+ private Plot plot = new NavigationPlot();
+
+ private bool transformChangeRequested = false;
+
+ ///
+ /// Initializes a new instance of class.
+ ///
+ public MouseNavigation()
+ {
+ Children.Add(navigationCanvas);
+
+
+ Loaded += MouseNavigationLoaded;
+ Unloaded += MouseNavigationUnloaded;
+
+ MouseLeave += new MouseEventHandler(MouseNavigationLayer_MouseLeave);
+ MouseMove += new MouseEventHandler(MouseNavigationLayer_MouseMove);
+ MouseLeftButtonUp += new MouseButtonEventHandler(MouseNavigationLayer_MouseLeftButtonUp);
+ MouseLeftButtonDown += new MouseButtonEventHandler(MouseNavigationLayer_MouseLeftButtonDown);
+ MouseWheel += new MouseWheelEventHandler(MouseNavigationLayer_MouseWheel);
+
+ LayoutUpdated += (s, a) => transformChangeRequested = false;
+ }
+
+ /// Gets or sets vertical navigation status. True means that user can navigate along Y axis
+ /// The default value is true
+ [Category("InteractiveDataDisplay")]
+ public bool IsVerticalNavigationEnabled
+ {
+ get { return (bool)GetValue(IsVerticalNavigationEnabledProperty); }
+ set { SetValue(IsVerticalNavigationEnabledProperty, value); }
+ }
+
+ /// Identifies property
+ public static readonly DependencyProperty IsVerticalNavigationEnabledProperty =
+ DependencyProperty.Register("IsVerticalNavigationEnabled", typeof(bool), typeof(MouseNavigation), new PropertyMetadata(true));
+
+ /// Gets or sets horizontal navigation status. True means that user can navigate along X axis
+ /// The default value is true
+ [Category("InteractiveDataDisplay")]
+ public bool IsHorizontalNavigationEnabled
+ {
+ get { return (bool)GetValue(IsHorizontalNavigationEnabledProperty); }
+ set { SetValue(IsHorizontalNavigationEnabledProperty, value); }
+ }
+
+ /// Identifies property
+ public static readonly DependencyProperty IsHorizontalNavigationEnabledProperty =
+ DependencyProperty.Register("IsHorizontalNavigationEnabled", typeof(bool), typeof(MouseNavigation), new PropertyMetadata(true));
+
+ void MouseNavigationUnloaded(object sender, RoutedEventArgs e)
+ {
+ if (masterPlot != null)
+ {
+ masterPlot.MouseLeave -= MouseNavigationLayer_MouseLeave;
+ masterPlot.MouseMove -= MouseNavigationLayer_MouseMove;
+ masterPlot.MouseLeftButtonUp -= MouseNavigationLayer_MouseLeftButtonUp;
+ masterPlot.MouseLeftButtonDown -= MouseNavigationLayer_MouseLeftButtonDown;
+ masterPlot.MouseWheel -= MouseNavigationLayer_MouseWheel;
+ }
+
+ masterPlot = null;
+ parentControl = null;
+ }
+
+ void MouseNavigationLoaded(object sender, RoutedEventArgs e)
+ {
+ masterPlot = PlotBase.FindMaster(this);
+
+ if (masterPlot != null)
+ {
+ masterPlot.MouseLeave += MouseNavigationLayer_MouseLeave;
+ masterPlot.MouseMove += MouseNavigationLayer_MouseMove;
+ masterPlot.MouseLeftButtonUp += MouseNavigationLayer_MouseLeftButtonUp;
+ masterPlot.MouseLeftButtonDown += MouseNavigationLayer_MouseLeftButtonDown;
+ masterPlot.MouseWheel += MouseNavigationLayer_MouseWheel;
+ }
+
+ var parent = VisualTreeHelper.GetParent(this);
+ var controlParent = parent as Control;
+ while (parent != null && controlParent == null)
+ {
+ parent = VisualTreeHelper.GetParent(parent);
+ controlParent = parent as Control;
+ }
+ parentControl = controlParent;
+ }
+
+ ///
+ /// Measures the size in layout required for child elements and determines a size for the Figure.
+ ///
+ /// The available size that this element can give to child elements. Infinity can be specified as a value to indicate that the element will size to whatever content is available.
+ /// The size that this element determines it needs during layout, based on its calculations of child element sizes.
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ navigationCanvas.Measure(availableSize);
+ return navigationCanvas.DesiredSize;
+ }
+
+ ///
+ /// Positions child elements and determines a size for a Figure
+ ///
+ /// The final area within the parent that Figure should use to arrange itself and its children
+ /// The actual size used
+ protected override Size ArrangeOverride(Size finalSize)
+ {
+ navigationCanvas.Arrange(new Rect(new Point(0, 0), finalSize));
+ transformChangeRequested = false;
+ return finalSize;
+ }
+
+ private void DoZoom(double factor)
+ {
+ if (masterPlot != null)
+ {
+ var rect = masterPlot.PlotRect;
+
+ if (IsHorizontalNavigationEnabled)
+ rect.X = rect.X.Zoom(factor);
+ if (IsVerticalNavigationEnabled)
+ rect.Y = rect.Y.Zoom(factor);
+
+ if (IsZoomEnable(rect))
+ {
+ masterPlot.SetPlotRect(rect);
+ masterPlot.IsAutoFitEnabled = false;
+ }
+ }
+ }
+
+ private bool IsZoomEnable(DataRect rect)
+ {
+ bool res = true;
+ if (IsHorizontalNavigationEnabled)
+ {
+ double e_max = Math.Log(Math.Max(Math.Abs(rect.XMax), Math.Abs(rect.XMin)), 2);
+ double log = Math.Log(rect.Width, 2);
+ res = log > e_max - 40;
+ }
+ if (IsVerticalNavigationEnabled)
+ {
+ double e_max = Math.Log(Math.Max(Math.Abs(rect.YMax), Math.Abs(rect.YMin)), 2);
+ double log = Math.Log(rect.Height, 2);
+ res = res && log > e_max - 40;
+ }
+ return res;
+ }
+
+ private void MouseNavigationLayer_MouseWheel(object sender, MouseWheelEventArgs e)
+ {
+ Point cursorPosition = e.GetPosition(this);
+ if (!CheckCursor(cursorPosition))
+ return;
+
+ // TODO: Zoom relative to mouse position
+ double factor = e.Delta < 0 ? 1.2 : 1 / 1.2;
+ DoZoom(factor);
+ e.Handled = true;
+ }
+
+ private void DoPan(Point screenStart, Point screenEnd)
+ {
+ if (masterPlot != null)
+ {
+ double dx = IsHorizontalNavigationEnabled ?
+ masterPlot.XFromLeft(screenEnd.X) - masterPlot.XFromLeft(screenStart.X) : 0;
+ double dy = IsVerticalNavigationEnabled ?
+ masterPlot.YFromTop(screenEnd.Y) - masterPlot.YFromTop(screenStart.Y) : 0;
+ var rect = masterPlot.PlotRect;
+
+ double width = rect.Width;
+ double height = rect.Height;
+
+ masterPlot.SetPlotRect(new DataRect(
+ rect.XMin - dx,
+ rect.YMin - dy,
+ rect.XMin - dx + width,
+ rect.YMin - dy + height));
+
+ masterPlot.IsAutoFitEnabled = false;
+ }
+ }
+
+ private void MouseNavigationLayer_MouseLeave(object sender, MouseEventArgs e)
+ {
+ if (isPanning && (e.OriginalSource == navigationCanvas || e.OriginalSource == masterPlot))
+ {
+ //panningEnd = e.GetPosition(this);
+ DoPan(panningStart, panningEnd);
+ isPanning = false;
+ }
+ if (isSelecting && (e.OriginalSource == navigationCanvas || e.OriginalSource == masterPlot))
+ {
+ if (selectArea != null)
+ {
+ if (navigationCanvas.Children.Contains(selectArea))
+ {
+ navigationCanvas.Children.Remove(selectArea);
+ }
+
+ if (masterPlot != null && masterPlot.Children.Contains(plot))
+ {
+ masterPlot.Children.Remove(plot);
+ }
+ }
+ isSelecting = false;
+ selectingStarted = false;
+ }
+ }
+
+
+
+ private void MouseNavigationLayer_MouseMove(object sender, MouseEventArgs e)
+ {
+ Point cursorPosition = e.GetPosition(this);
+ if (!CheckCursor(cursorPosition))
+ {
+ wasInside = true;
+ MouseNavigationLayer_MouseLeave(sender, e);
+ return;
+ }
+ else
+ {
+ if (wasInside && isLeftClicked)
+ {
+ HandleMouseUp();
+ HandleMouseDown(e);
+ }
+ }
+
+ if (isPanning)
+ {
+ if (!transformChangeRequested)
+ {
+ transformChangeRequested = true;
+ panningEnd = e.GetPosition(this);
+ DoPan(panningStart, panningEnd);
+ panningStart = panningEnd;
+ InvalidateArrange();
+ }
+ }
+
+ if (isSelecting)
+ {
+ selectEnd = e.GetPosition(this);
+ if (!selectingStarted)
+ {
+ if ((Math.Abs(selectStart.X - selectEnd.X) > 4) || (Math.Abs(selectStart.Y - selectEnd.Y) > 4))
+ {
+ selectingStarted = true;
+ selectArea = new Rectangle();
+ selectArea.Fill = new SolidColorBrush(Colors.LightGray);
+ selectArea.StrokeThickness = 2;
+ selectArea.Stroke = new SolidColorBrush(Color.FromArgb(255, 40, 0, 120));
+ selectArea.Opacity = 0.6;
+
+ if (masterPlot != null)
+ {
+ plot.IsAutoFitEnabled = false;
+
+ plot.Children.Clear();
+ plot.Children.Add(selectArea);
+
+ if (!masterPlot.Children.Contains(plot))
+ {
+ masterPlot.Children.Add(plot);
+ Canvas.SetZIndex(plot, 40000);
+ }
+
+ double width = Math.Abs(masterPlot.XFromLeft(selectStart.X) - masterPlot.XFromLeft(selectEnd.X));
+ double height = Math.Abs(masterPlot.YFromTop(selectStart.Y) - masterPlot.YFromTop(selectEnd.Y));
+ double xmin = masterPlot.XFromLeft(Math.Min(selectStart.X, selectEnd.X));
+ double ymin = masterPlot.YFromTop(Math.Max(selectStart.Y, selectEnd.Y));
+
+ Plot.SetX1(selectArea, xmin);
+ Plot.SetY1(selectArea, ymin);
+ Plot.SetX2(selectArea, xmin + width);
+ Plot.SetY2(selectArea, ymin + height);
+
+ }
+ else
+ {
+ navigationCanvas.Children.Add(selectArea);
+ Canvas.SetLeft(selectArea, selectStart.X);
+ Canvas.SetTop(selectArea, selectStart.Y);
+ }
+ }
+ }
+ else
+ {
+ if (masterPlot == null)
+ {
+ double width = Math.Abs(selectStart.X - selectEnd.X);
+ double height = Math.Abs(selectStart.Y - selectEnd.Y);
+ selectArea.Width = width;
+ selectArea.Height = height;
+
+ if (selectEnd.X < selectStart.X)
+ {
+ Canvas.SetLeft(selectArea, selectStart.X - width);
+ }
+
+ if (selectEnd.Y < selectStart.Y)
+ {
+ Canvas.SetTop(selectArea, selectStart.Y - height);
+ }
+ }
+ else
+ {
+ double width = Math.Abs(masterPlot.XFromLeft(selectStart.X) - masterPlot.XFromLeft(selectEnd.X));
+ double height = Math.Abs(masterPlot.YFromTop(selectStart.Y) - masterPlot.YFromTop(selectEnd.Y));
+ double xmin = masterPlot.XFromLeft(Math.Min(selectStart.X, selectEnd.X));
+ double ymin = masterPlot.YFromTop(Math.Max(selectStart.Y, selectEnd.Y));
+
+ Plot.SetX1(selectArea, xmin);
+ Plot.SetY1(selectArea, ymin);
+ Plot.SetX2(selectArea, xmin + width);
+ Plot.SetY2(selectArea, ymin + height);
+ }
+ }
+ }
+
+ wasInside = false;
+ }
+
+ private void MouseNavigationLayer_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+ {
+ e.Handled = HandleMouseDown(e);
+ }
+
+ private void MouseNavigationLayer_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
+ {
+ e.Handled = HandleMouseUp();
+ }
+
+ private bool CheckCursor(Point cursorPosition)
+ {
+ return !(cursorPosition.X < 0 || cursorPosition.Y < 0 || cursorPosition.X > this.ActualWidth || cursorPosition.Y > this.ActualHeight);
+ }
+
+ private bool HandleMouseDown(MouseEventArgs e)
+ {
+ Point cursorPosition = e.GetPosition(this);
+ if (!CheckCursor(cursorPosition))
+ return false;
+ else
+ {
+ isLeftClicked = true;
+
+ if (masterPlot != null)
+ {
+ if (Keyboard.Modifiers == ModifierKeys.Shift)
+ {
+ DoZoom(1.2);
+ }
+ else
+ {
+ if (Keyboard.Modifiers == ModifierKeys.Control)
+ {
+ if (selectingMode && IsVerticalNavigationEnabled && IsHorizontalNavigationEnabled)
+ {
+ isSelecting = true;
+ selectStart = cursorPosition;
+ this.CaptureMouse();
+ }
+ lastClick = DateTime.Now;
+ }
+ else
+ {
+ DateTime d = DateTime.Now;
+ if ((d - lastClick).TotalMilliseconds < 200)
+ {
+ masterPlot.IsAutoFitEnabled = true;
+ return true;
+ }
+ else
+ {
+
+ isPanning = true;
+ panningStart = cursorPosition;
+ this.CaptureMouse();
+ lastClick = DateTime.Now;
+
+ }
+ }
+ }
+ }
+ }
+
+ if (parentControl != null)
+ parentControl.Focus();
+
+ return true;
+ }
+
+ private bool HandleMouseUp()
+ {
+ isLeftClicked = false;
+ isPanning = false;
+ if ((!isSelecting || !selectingStarted) && (Keyboard.Modifiers == ModifierKeys.Control))
+ {
+ DateTime d = DateTime.Now;
+ if ((d - lastClick).TotalMilliseconds < 300)
+ {
+ isSelecting = false;
+ DoZoom(1 / 1.2);
+ }
+ }
+ if (isSelecting)
+ {
+ if (selectingStarted)
+ {
+ if (!transformChangeRequested)
+ {
+ transformChangeRequested = true;
+ isSelecting = false;
+ selectingStarted = false;
+ navigationCanvas.Children.Remove(selectArea);
+
+ if (masterPlot != null)
+ {
+ masterPlot.Children.Remove(plot);
+
+ double width = Math.Abs(masterPlot.XFromLeft(selectStart.X) - masterPlot.XFromLeft(selectEnd.X));
+ double height = Math.Abs(masterPlot.YFromTop(selectStart.Y) - masterPlot.YFromTop(selectEnd.Y));
+ double xmin = masterPlot.XFromLeft(Math.Min(selectStart.X, selectEnd.X));
+ double ymin = masterPlot.YFromTop(Math.Max(selectStart.Y, selectEnd.Y));
+
+ masterPlot.SetPlotRect(new DataRect(xmin, ymin, xmin + width, ymin + height));
+ masterPlot.IsAutoFitEnabled = false;
+ }
+ }
+ }
+ }
+ this.ReleaseMouseCapture();
+
+ return true;
+ }
+
+ }
+
+
+ internal class NavigationPlot : Plot
+ {
+ protected override DataRect ComputeBounds()
+ {
+ return DataRect.Empty;
+ }
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Palette/Palette.Converters.cs b/InteractiveDataDisplay_OneDll/src/Palette/Palette.Converters.cs
new file mode 100644
index 000000000..3013c37bd
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Palette/Palette.Converters.cs
@@ -0,0 +1,130 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Windows.Data;
+using System.Diagnostics;
+using System.ComponentModel;
+using System.Globalization;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Converts a string to a new instance of class.
+ ///
+ public class StringToPaletteConverter : IValueConverter
+ {
+ ///
+ /// Parses an string to . For details see method.
+ ///
+ /// A string to parse.
+ ///
+ ///
+ ///
+ /// A palette that this string describes.
+ public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+ {
+ try
+ {
+ string str = (string)value;
+ return Palette.Parse(str);
+ }
+ catch (Exception exc)
+ {
+ Debug.WriteLine("StringToPaletteConverter: " + exc.Message);
+ return Palette.Parse("Black");
+ }
+ }
+
+ ///
+ /// Returns a string describing an instance of class.
+ ///
+ /// An instance of class.
+ ///
+ ///
+ ///
+ /// A string describing specified palette.
+ public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+ {
+ Palette palette = (Palette)value;
+ return palette.ToString();
+ }
+ }
+
+ ///
+ /// Provides a way to convert strings defined in xaml to a new instance of class.
+ ///
+ public class StringToPaletteTypeConverter : TypeConverter
+ {
+ ///
+ /// Gets whether a value can be converted to .
+ ///
+ /// A format context.
+ /// A type to convert from.
+ /// True if a value can be converted, false otherwise.
+ public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
+ {
+ return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
+ }
+
+ ///
+ /// Parses an string to . For details see method.
+ ///
+ ///
+ ///
+ /// A string to parse.
+ /// A palette that this string describes.
+ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+ {
+ if (value is string)
+ {
+ try
+ {
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ string str = value.ToString();
+ return Palette.Parse(str);
+ }
+ catch (Exception exc)
+ {
+ Debug.WriteLine("StringToPaletteConverter: " + exc.Message);
+ return Palette.Parse("Black");
+ }
+ }
+ else
+ return base.ConvertFrom(context, culture, value);
+ }
+
+ ///
+ /// Returns whether a value can be converted to the specified type.
+ ///
+ /// A format context.
+ /// A type to convert to.
+ /// True if the value can be converted, false otherwise
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ return destinationType == typeof(string) || base.CanConvertTo(destinationType);
+ }
+
+ ///
+ /// Returns a string describing an instance of class.
+ ///
+ ///
+ ///
+ /// An instance of class.
+ ///
+ /// A string describing specified palette.
+ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
+ {
+ var palette = value as Palette;
+ if (palette != null)
+ {
+ return palette.ToString();
+ }
+ else
+ return base.ConvertTo(context, destinationType);
+ }
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Palette/PaletteControl.cs b/InteractiveDataDisplay_OneDll/src/Palette/PaletteControl.cs
new file mode 100644
index 000000000..d91698ca7
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Palette/PaletteControl.cs
@@ -0,0 +1,217 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.ComponentModel;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// A control to show all the information about .
+ ///
+ [Description("Visually maps value to color")]
+ public class PaletteControl : ContentControl
+ {
+ #region Fields
+
+ private Image image;
+ private Axis axis;
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets or sets the palette.
+ /// Default value is black palette.
+ ///
+ [Category("InteractiveDataDisplay")]
+ [TypeConverter(typeof(StringToPaletteTypeConverter))]
+ public Palette Palette
+ {
+ get { return (Palette)GetValue(PaletteProperty); }
+ set { SetValue(PaletteProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the range of axis values.
+ /// Default value is [0, 1].
+ ///
+ [Category("InteractiveDataDisplay")]
+ [Description("Range of values mapped to color")]
+ public Range Range
+ {
+ get { return (Range)GetValue(RangeProperty); }
+ set { SetValue(RangeProperty, value); }
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty PaletteProperty = DependencyProperty.Register(
+ "Palette",
+ typeof(Palette),
+ typeof(PaletteControl),
+ new PropertyMetadata(Palette.Parse("Black"), OnPaletteChanged));
+
+ private static void OnPaletteChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ PaletteControl control = (PaletteControl)d;
+ control.OnPaletteChanged();
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty RangeProperty = DependencyProperty.Register(
+ "Range",
+ typeof(Range),
+ typeof(PaletteControl),
+ new PropertyMetadata(new Range(0, 1), OnRangeChanged));
+
+ ///
+ /// Gets or sets a value indicating whether an axis should be displayed.
+ /// Default value is true.
+ ///
+ [Category("InteractiveDataDisplay")]
+ public bool IsAxisVisible
+ {
+ get { return (bool)GetValue(IsAxisVisibleProperty); }
+ set { SetValue(IsAxisVisibleProperty, value); }
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty IsAxisVisibleProperty = DependencyProperty.Register(
+ "IsAxisVisible",
+ typeof(bool),
+ typeof(PaletteControl),
+ new PropertyMetadata(true));
+
+ ///
+ /// Gets or sets the height of rendered palette.
+ /// Default value is 20.
+ ///
+ [Category("InteractiveDataDisplay")]
+ public double PaletteHeight
+ {
+ get { return (double)GetValue(PaletteHeightProperty); }
+ set { SetValue(PaletteHeightProperty, value); }
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty PaletteHeightProperty = DependencyProperty.Register(
+ "PaletteHeight",
+ typeof(double),
+ typeof(PaletteControl),
+ new PropertyMetadata(20.0, OnPaletteHeightChanged));
+
+ private static void OnPaletteHeightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ PaletteControl control = (PaletteControl)d;
+ control.image.Height = (double)e.NewValue;
+ control.UpdateBitmap();
+ }
+
+ #endregion
+
+ #region ctor
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public PaletteControl()
+ {
+ StackPanel stackPanel = new StackPanel();
+
+ image = new Image { Height = 20, Stretch = Stretch.None, HorizontalAlignment = HorizontalAlignment.Stretch };
+ axis = new Axis { AxisOrientation = AxisOrientation.Bottom, HorizontalAlignment = HorizontalAlignment.Stretch };
+
+ stackPanel.Children.Add(image);
+ stackPanel.Children.Add(axis);
+
+ Content = stackPanel;
+
+ SizeChanged += (o, e) =>
+ {
+ if (e.PreviousSize.Width == 0 || e.PreviousSize.Height == 0 || Double.IsNaN(e.PreviousSize.Width) || Double.IsNaN(e.PreviousSize.Height))
+ UpdateBitmap();
+ };
+
+ IsTabStop = false;
+ }
+
+ #endregion
+
+ #region Private methods
+
+ private void OnPaletteChanged()
+ {
+ UpdateBitmap();
+ }
+
+ private void UpdateBitmap()
+ {
+ if (Width == 0 || Double.IsNaN(Width))
+ {
+ image.Source = null;
+ return;
+ }
+ if (Palette == null)
+ {
+ image.Source = null;
+ return;
+ }
+
+ int width = (int)Width;
+ int height = (int)image.Height;
+ WriteableBitmap bmp2 = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgra32, null);
+ WriteableBitmap bmp = bmp2.Clone();
+ bmp.Lock();
+ unsafe
+ {
+ byte* pixels = (byte*)bmp.BackBuffer;
+ int stride = bmp.BackBufferStride;
+ int pixelWidth = bmp.PixelWidth;
+ double min = Palette.Range.Min;
+ double coeff = (Palette.Range.Max - min) / bmp.PixelWidth;
+ for (int i = 0; i < pixelWidth; i++)
+ {
+ double ratio = i * coeff + min;
+ Color color = Palette.GetColor(i * coeff + min);
+ for (int j = 0; j < height; j++)
+ {
+ pixels[(i << 2) + 3 + j * stride] = color.A;
+ pixels[(i << 2) + 2 + j * stride] = color.R;
+ pixels[(i << 2) + 1 + j * stride] = color.G;
+ pixels[(i << 2) + j * stride] = color.B;
+ }
+ }
+ }
+ bmp.Unlock();
+ image.Source = bmp;
+ }
+
+ private static void OnRangeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ PaletteControl control = (PaletteControl)d;
+ control.OnRangeChanged();
+ }
+
+ private void OnRangeChanged()
+ {
+ axis.Range = Range;
+ }
+
+ #endregion
+
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/PlotBase.cs b/InteractiveDataDisplay_OneDll/src/PlotBase.cs
new file mode 100644
index 000000000..e4180dd5b
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/PlotBase.cs
@@ -0,0 +1,947 @@
+// Copyright © Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Collections.Generic;
+using System.Linq;
+using System.ComponentModel;
+
+namespace InteractiveDataDisplay.WPF
+{
+ /// Represents a base for all elements that perform graphic plotting. Handles coordinate
+ /// transformations and manages composition of plots
+ public abstract class PlotBase : Panel
+ {
+ #region Fields
+
+ private double scaleXField;
+ private double scaleYField;
+ private double offsetXField;
+ private double offsetYField;
+
+ private bool isInternalChange = false;
+
+ private DataRect actualPlotRect = new DataRect(0, 0, 1, 1);
+
+ #endregion
+
+ #region Properties
+
+ /// Gets or sets padding - distance in screen units from each side of border to edges of plot bounding rectangle.
+ /// Effective padding for composition of plots is computed as maximum of all paddings.
+ [Category("InteractiveDataDisplay")]
+ public Thickness Padding
+ {
+ get { return (Thickness)GetValue(PaddingProperty); }
+ set { SetValue(PaddingProperty, value); }
+ }
+
+ ///
+ /// Desired ratio of horizontal scale to vertical scale. Values less than or equal to zero
+ /// represent unspecified aspect ratio.
+ ///
+ [Category("InteractiveDataDisplay")]
+ public double AspectRatio
+ {
+ get { return (double)GetValue(AspectRatioProperty); }
+ set { SetValue(AspectRatioProperty, value); }
+ }
+
+ ///
+ /// Gets or sets auto fit mode. property of all
+ /// plots in compositions are updated instantly to have same value.
+ ///
+ [Category("InteractiveDataDisplay")]
+ public bool IsAutoFitEnabled
+ {
+ get { return (bool)GetValue(IsAutoFitEnabledProperty); }
+ set { SetValue(IsAutoFitEnabledProperty, value); }
+ }
+
+ /// Gets or sets desired plot width in plot coordinates. property of all
+ /// plots in compositions are updated instantly to have same value. Settings this property
+ /// turns off auto fit mode.
+ [Category("InteractiveDataDisplay")]
+ public double PlotWidth
+ {
+ get { return (double)GetValue(PlotWidthProperty); }
+ set { SetValue(PlotWidthProperty, value); }
+ }
+
+ /// Gets or sets desired plot height in plot coordinates. property of all
+ /// plots in compositions are updated instantly to have same value. Settings this property
+ /// turns off auto fit mode.
+ [Category("InteractiveDataDisplay")]
+ public double PlotHeight
+ {
+ get { return (double)GetValue(PlotHeightProperty); }
+ set { SetValue(PlotHeightProperty, value); }
+ }
+
+ /// Gets or sets desired minimal visible horizontal coordinate (in plot coordinate system).
+ /// property of all
+ /// plots in compositions are updated instantly to have same value. Settings this property
+ /// turns off auto fit mode.
+ [Category("InteractiveDataDisplay")]
+ public double PlotOriginX
+ {
+ get { return (double)GetValue(PlotOriginXProperty); }
+ set { SetValue(PlotOriginXProperty, value); }
+ }
+
+ /// Gets or sets desired minimal visible vertical coordinate (in plot coordinate system).
+ /// property of all
+ /// plots in compositions are updated instantly to have same value. Settings this property
+ /// turns off auto fit mode.
+ [Category("InteractiveDataDisplay")]
+ public double PlotOriginY
+ {
+ get { return (double)GetValue(PlotOriginYProperty); }
+ set { SetValue(PlotOriginYProperty, value); }
+ }
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty IsXAxisReversedProperty =
+ DependencyProperty.Register("IsXAxisReversed", typeof(bool), typeof(PlotBase), new PropertyMetadata(false));
+
+ ///
+ /// Gets or sets a flag indicating whether the x-axis is reversed or not.
+ ///
+ [Category("InteractiveDataDisplay")]
+ public bool IsXAxisReversed
+ {
+ get { return (bool)GetValue(IsXAxisReversedProperty); }
+ set { SetValue(IsXAxisReversedProperty, value); }
+ }
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty IsYAxisReversedProperty =
+ DependencyProperty.Register("IsYAxisReversed", typeof(bool), typeof(PlotBase), new PropertyMetadata(false));
+
+ ///
+ /// Gets or sets a flag indicating whether the y-axis is reversed or not.
+ ///
+ [Category("InteractiveDataDisplay")]
+ public bool IsYAxisReversed
+ {
+ get { return (bool)GetValue(IsYAxisReversedProperty); }
+ set { SetValue(IsYAxisReversedProperty, value); }
+ }
+
+ /// Gets or sets transform from user data to horizontal plot coordinate.
+ /// By default transform is
+ ///
+ [Category("InteractiveDataDisplay")]
+ public DataTransform XDataTransform
+ {
+ get { return (DataTransform)GetValue(XDataTransformProperty); }
+ set { SetValue(XDataTransformProperty, value); }
+ }
+
+ /// Gets or sets transform from user data to vertical plot coordinate.
+ /// By default transform is
+ ///
+ [Category("InteractiveDataDisplay")]
+ public DataTransform YDataTransform
+ {
+ get { return (DataTransform)GetValue(YDataTransformProperty); }
+ set { SetValue(YDataTransformProperty, value); }
+ }
+
+ ///
+ /// Gets or Sets horizontal scale parameter for plot transform
+ ///
+ public double ScaleX
+ {
+ get
+ {
+ return IsMaster ? scaleXField : masterField.ScaleX;
+ }
+ protected set
+ {
+ scaleXField = value;
+ }
+ }
+
+ ///
+ /// Gets or sets Horizontal offset for plot transform
+ ///
+ public double OffsetX
+ {
+ get
+ {
+ return IsMaster ? offsetXField : masterField.OffsetX;
+ }
+ protected set
+ {
+ offsetXField = value;
+ }
+ }
+
+ ///
+ /// Gets or Sets horizontal scale parameter for plot transform
+ ///
+ public double ScaleY
+ {
+ get
+ {
+ return IsMaster ? scaleYField : masterField.ScaleY;
+ }
+ protected set
+ {
+ scaleYField = value;
+ }
+ }
+
+ ///
+ /// Gets or sets Horizontal offset for plot transform
+ ///
+ public double OffsetY
+ {
+ get
+ {
+ return IsMaster ? offsetYField : masterField.OffsetY;
+ }
+ protected set
+ {
+ offsetYField = value;
+ }
+ }
+
+ /// Gets actual plot rectangle which is exactly corresponds to visible part of plot
+ public DataRect ActualPlotRect
+ {
+ get { return IsMaster ? actualPlotRect : masterField.ActualPlotRect; }
+ private set { actualPlotRect = value; }
+ }
+
+ /// Gets desired plot rectangle. Actual plot rectangle may be larger because of aspect ratio
+ /// constraint
+ public DataRect PlotRect
+ {
+ get { return new DataRect(PlotOriginX, PlotOriginY, PlotOriginX + PlotWidth, PlotOriginY + PlotHeight); }
+ }
+
+ private bool IsInternalChange
+ {
+ get { return IsMaster ? isInternalChange : masterField.IsInternalChange; }
+ set
+ {
+ if (IsMaster)
+ isInternalChange = value;
+ else
+ masterField.IsInternalChange = value;
+ }
+ }
+
+
+
+ #endregion
+
+ ///
+ /// Initializes new instance of class
+ ///
+ protected PlotBase()
+ {
+ XDataTransform = new IdentityDataTransform();
+ YDataTransform = new IdentityDataTransform();
+ masterField = this;
+ Loaded += PlotBaseLoaded;
+ Unloaded += PlotBaseUnloaded;
+ }
+
+
+ #region dependants tree
+
+ private List dependantsField = new List();
+ ///
+ /// Master PlotBase of current PlotBase
+ ///
+ protected PlotBase masterField;
+
+ private void AddDependant(PlotBase dependant)
+ {
+ if (!dependantsField.Contains(dependant))
+ {
+ dependantsField.Add(dependant);
+ EnumAll(plot => plot.NotifyCompositionChange());
+ }
+ }
+
+ private void RemoveDependant(PlotBase dependant)
+ {
+ if (dependantsField.Contains(dependant))
+ {
+ dependantsField.Remove(dependant);
+ EnumAll(plot => plot.NotifyCompositionChange());
+ }
+ }
+
+ private void connectMaster()
+ {
+ if (!forceMasterField)
+ {
+ var element = VisualTreeHelper.GetParent(this);
+ while (element != null && !(element is PlotBase))
+ element = VisualTreeHelper.GetParent(element);
+ if (element != null)
+ {
+ var newMaster = (PlotBase)element;
+
+ // Take plot-related properties from new master plot
+ IsInternalChange = true;
+ PlotOriginX = newMaster.PlotOriginX;
+ PlotOriginY = newMaster.PlotOriginY;
+ PlotWidth = newMaster.PlotWidth;
+ PlotHeight = newMaster.PlotHeight;
+ IsAutoFitEnabled = newMaster.IsAutoFitEnabled;
+ IsInternalChange = false;
+
+ masterField = newMaster;
+ masterField.AddDependant(this);
+ InvalidateBounds();
+ }
+ }
+ }
+
+ private void disconnectMaster()
+ {
+ if (masterField != this)
+ {
+ masterField.RemoveDependant(this);
+ InvalidateBounds();
+ masterField = this;
+ }
+ }
+
+ private void EnumLeaves(Action action)
+ {
+ action(this);
+ foreach (var item in dependantsField)
+ {
+ item.EnumLeaves(action);
+ }
+ }
+
+ private void EnumAll(Action action)
+ {
+ if (masterField != this)
+ {
+ if(masterField != null)
+ masterField.EnumAll(action);
+ }
+ else
+ EnumLeaves(action);
+ }
+
+ bool forceMasterField = false;
+
+ ///
+ /// Disables or enables the connection of the panel to its parent context.
+ ///
+ ///
+ /// The default value is false which allows automatically connecting to a parent master and switching to a dependant mode.
+ /// If set to true the connection is disabled and the pannel is always in a master mode.
+ ///
+ public bool ForceMaster
+ {
+ get { return forceMasterField; }
+ set
+ {
+ forceMasterField = value;
+ if (forceMasterField)
+ disconnectMaster();
+ else
+ connectMaster();
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the panel is a master mode or in a dependant mode.
+ ///
+ public bool IsMaster { get { return masterField == this; } }
+
+ private IEnumerable SelfAndAllDependantPlots
+ {
+ get
+ {
+ IEnumerable result = new PlotBase[] { this };
+ foreach (var item in dependantsField)
+ {
+ result = result.Concat(item.SelfAndAllDependantPlots);
+ }
+ return result;
+ }
+ }
+
+ ///
+ /// Gets collections of all related plots of current in composition
+ ///
+ public IEnumerable RelatedPlots
+ {
+ get
+ {
+ if (masterField != this)
+ return masterField.RelatedPlots;
+ else
+ {
+ IEnumerable result = new PlotBase[] { this };
+ foreach (var item in dependantsField)
+ {
+ result = result.Concat(item.SelfAndAllDependantPlots);
+ }
+ return result;
+ }
+ }
+ }
+
+
+ ///
+ /// Raises plots composition changed event
+ ///
+ protected void NotifyCompositionChange()
+ {
+ //compositionChange.OnNext(new PlotCompositionChange());
+ }
+
+ ///
+ /// Gets event which occures when plots composition is changed
+ ///
+ public IObservable CompositionChange
+ {
+ get { return null; }
+ }
+
+ #endregion
+
+ #region Dependency Properties
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty XDataTransformProperty =
+ DependencyProperty.Register("XDataTransform", typeof(DataTransform), typeof(PlotBase), new PropertyMetadata(null,
+ (o, e) =>
+ {
+ Plot plot = o as Plot;
+ if (plot != null)
+ {
+ plot.OnXDataTransformChanged(e);
+ }
+ }));
+
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty YDataTransformProperty =
+ DependencyProperty.Register("YDataTransform", typeof(DataTransform), typeof(PlotBase), new PropertyMetadata(null,
+ (o, e) =>
+ {
+ Plot plot = o as Plot;
+ if (plot != null)
+ {
+ plot.OnYDataTransformChanged(e);
+ }
+ }));
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty PaddingProperty =
+ DependencyProperty.Register("Padding", typeof(Thickness), typeof(PlotBase), new PropertyMetadata(new Thickness(0.0),
+ (o, e) =>
+ {
+ PlotBase plotBase = (PlotBase)o;
+ if (plotBase != null)
+ {
+ if (!plotBase.IsMaster)
+ {
+ plotBase.masterField.InvalidateMeasure();
+ }
+ else
+ plotBase.InvalidateMeasure();
+ }
+ }));
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty AspectRatioProperty =
+ DependencyProperty.Register("AspectRatio", typeof(double), typeof(PlotBase), new PropertyMetadata(0.0,
+ (o, e) =>
+ {
+ PlotBase plotBase = (PlotBase)o;
+ if (plotBase != null)
+ {
+ plotBase.InvalidateMeasure();
+ if (!plotBase.IsMaster)
+ {
+ plotBase.masterField.InvalidateMeasure();
+ }
+ }
+ }));
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty IsAutoFitEnabledProperty =
+ DependencyProperty.Register("IsAutoFitEnabled", typeof(bool), typeof(PlotBase), new PropertyMetadata(true,
+ (o, e) =>
+ {
+ PlotBase plotBase = (PlotBase)o;
+ if (plotBase != null)
+ {
+ plotBase.OnIsAutoFitEnabledChanged(e);
+ }
+ }));
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty PlotWidthProperty =
+ DependencyProperty.Register("PlotWidth", typeof(double), typeof(PlotBase), new PropertyMetadata(1.0,
+ (o, e) =>
+ {
+ PlotBase plotBase = (PlotBase)o;
+ if (plotBase != null)
+ {
+ plotBase.OnPlotWidthChanged(e);
+ }
+ }));
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty PlotHeightProperty =
+ DependencyProperty.Register("PlotHeight", typeof(double), typeof(PlotBase), new PropertyMetadata(1.0,
+ (o, e) =>
+ {
+ PlotBase plotBase = (PlotBase)o;
+ if (plotBase != null)
+ {
+ plotBase.OnPlotHeightChanged(e);
+ }
+ }));
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty PlotOriginXProperty =
+ DependencyProperty.Register("PlotOriginX", typeof(double), typeof(PlotBase), new PropertyMetadata(0.0,
+ (o, e) =>
+ {
+ PlotBase plotBase = (PlotBase)o;
+ if (plotBase != null)
+ {
+ plotBase.OnPlotOriginXChanged(e);
+ }
+ }));
+
+ ///
+ /// Identifies dependency property
+ ///
+ public static readonly DependencyProperty PlotOriginYProperty =
+ DependencyProperty.Register("PlotOriginY", typeof(double), typeof(PlotBase), new PropertyMetadata(0.0,
+ (o, e) =>
+ {
+ PlotBase plotBase = (PlotBase)o;
+ if (plotBase != null)
+ {
+ plotBase.OnPlotOriginYChanged(e);
+ }
+ }));
+
+ /// Enables or disables clipping of graphic elements that are outside plotting area
+ public bool ClipToBounds
+ {
+ get { return (bool)GetValue(ClipToBoundsProperty); }
+ set { SetValue(ClipToBoundsProperty, value); }
+ }
+
+ /// Identifies dependency property
+ public static readonly DependencyProperty ClipToBoundsProperty =
+ DependencyProperty.Register("ClipToBounds", typeof(bool), typeof(PlotBase), new PropertyMetadata(true,
+ (s, a) => ((PlotBase)s).OnClipToBoundsChanged(a)));
+
+ ///
+ /// Occurs when property changed
+ ///
+ /// PropertyChanged parameters
+ protected virtual void OnClipToBoundsChanged(DependencyPropertyChangedEventArgs args)
+ {
+ InvalidateMeasure();
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Handler for property changes
+ ///
+ protected virtual void OnPlotOriginXChanged(DependencyPropertyChangedEventArgs e)
+ {
+ if (!IsInternalChange)
+ {
+ IsInternalChange = true;
+ EnumAll(p =>
+ {
+ p.PlotOriginX = (double)e.NewValue;
+ p.IsAutoFitEnabled = false;
+ p.InvalidateMeasure();
+ });
+ IsInternalChange = false;
+ }
+ }
+
+ ///
+ /// Handler for property changes
+ ///
+ protected virtual void OnPlotOriginYChanged(DependencyPropertyChangedEventArgs e)
+ {
+ if (!IsInternalChange)
+ {
+ IsInternalChange = true;
+ EnumAll(p =>
+ {
+ p.PlotOriginY = (double)e.NewValue;
+ p.IsAutoFitEnabled = false;
+ p.InvalidateMeasure();
+ });
+ IsInternalChange = false;
+ }
+ }
+
+ ///
+ /// Handler for property changes
+ ///
+ protected virtual void OnPlotWidthChanged(DependencyPropertyChangedEventArgs e)
+ {
+ if (!IsInternalChange)
+ {
+ IsInternalChange = true;
+ EnumAll(p =>
+ {
+ p.PlotWidth = (double)e.NewValue;
+ p.IsAutoFitEnabled = false;
+ p.InvalidateMeasure();
+ });
+ IsInternalChange = false;
+ }
+ }
+
+ ///
+ /// Handler for property changes
+ ///
+ protected virtual void OnPlotHeightChanged(DependencyPropertyChangedEventArgs e)
+ {
+ if (!IsInternalChange)
+ {
+ IsInternalChange = true;
+ EnumAll(p =>
+ {
+ p.PlotHeight = (double)e.NewValue;
+ p.IsAutoFitEnabled = false;
+ p.InvalidateMeasure();
+ });
+ IsInternalChange = false;
+ }
+ }
+
+ ///
+ /// Handler for property changes
+ ///
+ protected virtual void OnXDataTransformChanged(DependencyPropertyChangedEventArgs e)
+ {
+ InvalidateBounds();
+ }
+
+ ///
+ /// Handler for property changes
+ ///
+ protected virtual void OnYDataTransformChanged(DependencyPropertyChangedEventArgs e)
+ {
+ InvalidateBounds();
+ }
+
+ private void OnIsAutoFitEnabledChanged(DependencyPropertyChangedEventArgs e)
+ {
+ if (!IsInternalChange)
+ {
+ IsInternalChange = true;
+ EnumAll(p =>
+ {
+ p.IsAutoFitEnabled = (bool)e.NewValue;
+ if (p.IsAutoFitEnabled)
+ p.InvalidateMeasure();
+ });
+ IsInternalChange = false;
+ }
+ }
+
+ ///
+ /// Gets the range of {x,y} plot coordinates that correspond to all elements produced by the plot.
+ ///
+ /// The structure that holds the ranges of {x} and {y} plot coordinates or the value.
+ protected virtual DataRect ComputeBounds()
+ {
+ return DataRect.Empty;
+ }
+
+ ///
+ /// Invalidates effective plot coordinate ranges. This usually schedules recalculation of plot layout.
+ ///
+ public virtual void InvalidateBounds()
+ {
+ EnumAll(p => p.InvalidateMeasure());
+ }
+
+ ///
+ /// A common padding used by all graphing elements in the dependant tree.
+ ///
+ /// This is computed by traversing the master-dependant tree and computing the maximum mapping.
+ public Thickness ActualPadding { get { return IsMaster ? AggregatePadding() : masterField.ActualPadding; } }
+
+ private double GetEffectiveAspectRatio()
+ {
+ double result = AspectRatio;
+ for (int i = 0; i < dependantsField.Count && result <= 0; i++)
+ result = dependantsField[i].GetEffectiveAspectRatio();
+ return result;
+ }
+
+ ///
+ /// Computes padding of current instance
+ ///
+ /// Padding of current instance
+ protected virtual Thickness ComputePadding()
+ {
+ return Padding;
+ }
+
+ ///
+ /// Computes padding with maximum values for each side from padding of current instance and of all child instances
+ ///
+ /// Padding with maximum values for each side from padding of current instance and of all child instances
+ protected virtual Thickness AggregatePadding()
+ {
+ Thickness result = ComputePadding();
+ foreach (var item in dependantsField)
+ {
+ Thickness their = item.AggregatePadding();
+ if (their.Left > result.Left) result.Left = their.Left;
+ if (their.Right > result.Right) result.Right = their.Right;
+ if (their.Top > result.Top) result.Top = their.Top;
+ if (their.Bottom > result.Bottom) result.Bottom = their.Bottom;
+ }
+ return result;
+ }
+
+ ///
+ /// Computes minimal plot rectangle which contains plot rectangles of current instance and of all child instances
+ ///
+ /// Minimal plot rectangle which contains plot rectangles of current instance and of all child instances
+ protected DataRect AggregateBounds()
+ {
+ DataRect result = ComputeBounds();
+ foreach (var item in dependantsField)
+ result.Surround(item.AggregateBounds());
+
+ return result;
+ }
+
+ /// This event is fired when transform from plot to screen coordinates changes.
+ public event EventHandler PlotTransformChanged;
+
+ ///
+ /// For a given range of {x,y} plot coordinates and a given screen size taking into account effective aspect ratio and effective padding computes navigation transform.
+ ///
+ /// The range of {x,y} plot coordinates that must fit into the screen.
+ /// The width and height of the screen that must fit plotBounds.
+ internal void Fit(DataRect plotBounds, Size screenSize)
+ {
+ var padding = AggregatePadding();
+ var screenWidth = Math.Max(1.0, screenSize.Width - padding.Left - padding.Right);
+ var screenHeight = Math.Max(1.0, screenSize.Height - padding.Top - padding.Bottom);
+ var plotWidth = plotBounds.X.IsEmpty ? 0.0 : plotBounds.X.Max - plotBounds.X.Min;
+ if (plotWidth <= 0) plotWidth = 1.0;
+ var plotHeight = plotBounds.Y.IsEmpty ? 0.0 : plotBounds.Y.Max - plotBounds.Y.Min;
+ if (plotHeight <= 0) plotHeight = 1.0;
+
+ ScaleX = screenWidth / plotWidth;
+ ScaleY = screenHeight / plotHeight;
+
+ var aspect = GetEffectiveAspectRatio();
+ if (aspect > 0)
+ {
+ if (aspect * ScaleY < ScaleX)
+ ScaleX = aspect * ScaleY;
+ else
+ ScaleY = ScaleX / aspect;
+ }
+
+ OffsetX = plotBounds.X.IsEmpty ? 0.0 : ((plotBounds.X.Min + plotBounds.X.Max) * ScaleX - screenWidth) * 0.5 - padding.Left;
+ OffsetY = plotBounds.Y.IsEmpty ? screenSize.Height : ((plotBounds.Y.Min + plotBounds.Y.Max) * ScaleY + screenHeight) * 0.5 + padding.Top;
+
+ ActualPlotRect = new DataRect(
+ XFromLeft(screenSize.Width > padding.Left + padding.Right ? 0 : screenSize.Width * 0.5 - 0.5),
+ YFromTop(screenSize.Height > padding.Top + padding.Bottom ? screenSize.Height : screenSize.Height * 0.5 + 0.5),
+ XFromLeft(screenSize.Width > padding.Left + padding.Right ? screenSize.Width : screenSize.Width * 0.5 + 0.5),
+ YFromTop(screenSize.Height > padding.Top + padding.Bottom ? 0 : screenSize.Height * 0.5 - 0.5));
+
+ EnumAll(p =>
+ {
+ if (p.PlotTransformChanged != null)
+ p.PlotTransformChanged(this, EventArgs.Empty);
+ });
+ }
+
+ ///
+ /// Sets plot rectangle for current instance of
+ ///
+ /// plot rectangle value that would be set for current instance of
+ /// Identifies that it is internal call
+ protected void SetPlotRect(DataRect plotRect, bool fromAutoFit)
+ {
+ IsInternalChange = true;
+
+ EnumAll(p =>
+ {
+ p.PlotOriginX = plotRect.XMin;
+ p.PlotOriginY = plotRect.YMin;
+ p.PlotWidth = plotRect.Width;
+ p.PlotHeight = plotRect.Height;
+ if (!fromAutoFit)
+ {
+ p.IsAutoFitEnabled = false;
+ p.InvalidateMeasure();
+ }
+ });
+
+ IsInternalChange = false;
+ }
+
+ ///
+ /// Sets plot rectangle for current instance of
+ ///
+ /// plot rectangle value that would be set for current instance of
+ public void SetPlotRect(DataRect plotRect)
+ {
+ SetPlotRect(plotRect, false);
+ }
+
+ ///
+ /// Performs horizontal transform from plot coordinates to screen coordinates
+ ///
+ /// Value in plot coordinates
+ /// Value in screen coordinates
+ public double LeftFromX(double x)
+ {
+ return ScaleX > 0 ? x * ScaleX - OffsetX : x;
+ }
+
+ ///
+ /// Performs vertical transform from plot coordinates to screen coordinates
+ ///
+ /// Value in plot coordinates
+ /// Value in screen coordinates
+ public double TopFromY(double y)
+ {
+ return ScaleY > 0 ? OffsetY - y * ScaleY : OffsetY - y;
+ }
+
+ ///
+ /// Horizontal transform from screen coordinates to plot coordinates
+ ///
+ /// Value in screen coordinates
+ /// Value in plot coordinates
+ public double XFromLeft(double left)
+ {
+ return ScaleX > 0 ? (left + OffsetX) / ScaleX : left;
+ }
+
+ ///
+ /// Vertical transform from screen coordinates to plot coordinates
+ ///
+ /// Value in screen coordinates
+ /// Value in plot coordinates
+ public double YFromTop(double top)
+ {
+ return ScaleY > 0 ? (OffsetY - top) / ScaleY : OffsetY - top;
+ }
+
+ ///
+ /// Finds master plot for specifyied element
+ ///
+ /// Element, which mater plot should be found
+ /// Master Plot for specified element
+ public static PlotBase FindMaster(DependencyObject element)
+ {
+ var result = VisualTreeHelper.GetParent(element);
+
+ while (result != null && !(result is PlotBase))
+ result = VisualTreeHelper.GetParent(result);
+
+ return result as PlotBase;
+ }
+
+ ///
+ /// Occurs when PlotBase is loaded
+ ///
+ protected virtual void PlotBaseLoaded(object sender, RoutedEventArgs e)
+ {
+ connectMaster();
+ }
+
+ ///
+ /// Occurs when PlotBase is unloaded
+ ///
+ protected virtual void PlotBaseUnloaded(object sender, RoutedEventArgs e)
+ {
+ disconnectMaster();
+ }
+
+ ///
+ /// Performs measure algorithm if current instance of is master
+ ///
+ /// Availible size for measure
+ /// Desired size for current plot
+ protected Size PerformAsMaster(Size availableSize)
+ {
+ if (double.IsNaN(availableSize.Width)
+ || double.IsNaN(availableSize.Height)
+ || double.IsInfinity(availableSize.Width)
+ || double.IsInfinity(availableSize.Height))
+ availableSize = new Size(100, 100);
+ if (IsMaster)
+ {
+ DataRect desiredRect;
+ if (IsAutoFitEnabled)
+ {
+ desiredRect = AggregateBounds();
+ if (desiredRect.IsEmpty)
+ desiredRect = new DataRect(0, 0, 1, 1);
+
+ SetPlotRect(desiredRect, true);
+ }
+ else // resize
+ desiredRect = PlotRect;
+ Fit(desiredRect, availableSize);
+ }
+ return availableSize;
+ }
+
+ #endregion
+
+
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/PlotCompositionChange.cs b/InteractiveDataDisplay_OneDll/src/PlotCompositionChange.cs
new file mode 100644
index 000000000..d0be0dce2
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/PlotCompositionChange.cs
@@ -0,0 +1,69 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+namespace InteractiveDataDisplay.WPF
+{
+ /// Identifies event when collection of related plots in composition change
+ public struct PlotCompositionChange
+ {
+ private PlotBase master;
+
+ ///
+ /// Initializes a new instance of
+ ///
+ /// Master of composition that change
+ public PlotCompositionChange(PlotBase master)
+ {
+ this.master = master;
+ }
+
+ /// Gets master plot of composition that change
+ public PlotBase Master
+ {
+ get { return master; }
+ }
+
+ ///
+ /// Determines whether the specified is equal to the current .
+ ///
+ /// The to compare with the current .
+ /// True if the specified is equal to the current , false otherwise.
+ public override bool Equals(object obj)
+ {
+ PlotCompositionChange operand = (PlotCompositionChange)obj;
+ return master == operand.master;
+ }
+
+ ///
+ /// Returns a value that indicates whether two specified values are equal.
+ ///
+ /// The first value to compare.
+ /// The second value to compare.
+ /// True if values are equal, false otherwise.
+ public static bool operator ==(PlotCompositionChange plotCompositionChange1, PlotCompositionChange plotCompositionChange2)
+ {
+ return plotCompositionChange1.Equals(plotCompositionChange2);
+ }
+
+ ///
+ /// Returns a value that indicates whether two specified values are not equal.
+ ///
+ /// The first value to compare.
+ /// The second value to compare.
+ /// True if values are not equal, false otherwise.
+ public static bool operator !=(PlotCompositionChange plotCompositionChange1, PlotCompositionChange plotCompositionChange2)
+ {
+ return !plotCompositionChange1.Equals(plotCompositionChange2);
+ }
+
+ ///
+ /// Returns the hash code for this instance.
+ ///
+ /// The hash code for current instance
+ public override int GetHashCode()
+ {
+ return base.GetHashCode();
+ }
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Plots/Base/BackgroundBitmapRenderer.cs b/InteractiveDataDisplay_OneDll/src/Plots/Base/BackgroundBitmapRenderer.cs
new file mode 100644
index 000000000..cb532b05e
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Plots/Base/BackgroundBitmapRenderer.cs
@@ -0,0 +1,332 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Collections.Generic;
+using System.Threading;
+using System.ComponentModel;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Base class for renderers, which can prepare its render results in a separate thread.
+ ///
+ public abstract class BackgroundBitmapRenderer : Plot
+ {
+ private Image outputImage;
+
+ /// Cartesian coordinates of image currently on the screen.
+ private DataRect imageCartesianRect;
+
+ /// Size of image currently on the screen.
+ private Size imageSize;
+
+ private int maxTasks;
+ private Queue tasks = new Queue();
+ private delegate void RenderFunc(RenderResult r, RenderTaskState state);
+ private List runningTasks;
+
+ private long nextID = 0;
+
+ ///
+ /// Initializes new instance of class, performing all basic preparings for inheriting classes.
+ ///
+ protected BackgroundBitmapRenderer()
+ {
+ maxTasks = Math.Max(1, System.Environment.ProcessorCount - 1);
+ runningTasks = new List();
+
+ outputImage = new Image();
+ outputImage.Stretch = Stretch.None;
+ outputImage.VerticalAlignment = System.Windows.VerticalAlignment.Center;
+ outputImage.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
+
+ Children.Add(outputImage);
+ Unloaded += BackgroundBitmapRendererUnloaded;
+ }
+
+ void BackgroundBitmapRendererUnloaded(object sender, RoutedEventArgs e)
+ {
+ CancelAll();
+ }
+
+ private Size prevSize = new Size(Double.NaN, Double.NaN);
+ private double prevScaleX = Double.NaN, prevScaleY = Double.NaN,
+ prevOffsetX = Double.NaN, prevOffsetY = Double.NaN;
+
+ ///
+ /// Measures the size in layout required for child elements and determines a size for parent.
+ ///
+ /// The available size that this element can give to child elements. Infinity can be specified as a value to indicate that the element will size to whatever content is available.
+ /// The size that this element determines it needs during layout, based on its calculations of child element sizes.
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ availableSize = base.MeasureOverride(availableSize);
+
+ outputImage.Measure(availableSize);
+
+ // From resize or navigation?
+ if (prevSize != availableSize ||
+ prevOffsetX != masterField.OffsetX || prevOffsetY != masterField.OffsetY ||
+ prevScaleX != masterField.ScaleX || prevScaleY != masterField.ScaleY)
+ {
+ prevSize = availableSize;
+ prevOffsetX = masterField.OffsetX;
+ prevOffsetY = masterField.OffsetY;
+ prevScaleX = masterField.ScaleX;
+ prevScaleY = masterField.ScaleY;
+
+ CancelAll();
+
+ if (imageSize.Width > 0 && imageSize.Height > 0)
+ {
+ var newLT = new Point(LeftFromX(imageCartesianRect.XMin), TopFromY(imageCartesianRect.YMax));
+ var newRB = new Point(LeftFromX(imageCartesianRect.XMax), TopFromY(imageCartesianRect.YMin));
+ Canvas.SetLeft(outputImage, newLT.X);
+ Canvas.SetTop(outputImage, newLT.Y);
+ outputImage.RenderTransform = new ScaleTransform
+ {
+ ScaleX = (newRB.X - newLT.X) / imageSize.Width,
+ ScaleY = (newRB.Y - newLT.Y) / imageSize.Height
+ };
+ }
+ QueueRenderTask();
+ }
+
+ return availableSize;
+ }
+
+ private void EnqueueTask(long id/*Func task*/)
+ {
+ if (runningTasks.Count < maxTasks)
+ {
+ Size screenSize = new Size(Math.Abs(LeftFromX(ActualPlotRect.XMax) - LeftFromX(ActualPlotRect.XMin)), Math.Abs(TopFromY(ActualPlotRect.YMax) - TopFromY(ActualPlotRect.YMin)));
+ RenderTaskState state = new RenderTaskState(ActualPlotRect, screenSize);
+ state.Id = id;
+ state.Bounds = ComputeBounds();
+ runningTasks.Add(state);
+
+ if (!DesignerProperties.GetIsInDesignMode(this))
+ {
+ ThreadPool.QueueUserWorkItem(s =>
+ {
+ var rr = RenderFrame((RenderTaskState)s);
+ Dispatcher.BeginInvoke(new RenderFunc(OnTaskCompleted), rr, s);
+
+ }, state);
+ }
+ else
+ {
+ var rr = RenderFrame(state);
+ OnTaskCompleted(rr, state);
+ }
+ }
+ else
+ tasks.Enqueue(id);
+ }
+
+ ///
+ /// Renders frame and returns it as a render result.
+ ///
+ /// Render task state for rendering frame.
+ /// Render result of rendered frame.
+ protected virtual RenderResult RenderFrame(RenderTaskState state)
+ {
+ //if (!state.IsCancelled)
+ // return new RenderResult(HeatMap.BuildHeatMap(state.Transform.ScreenRect, new DataRect(state.Transform.ViewportRect), DataSource.X, DataSource.Y, DataSource.Data, 0));
+ //else
+ return null;
+ }
+
+ /// Creates new render task and puts it to queue.
+ /// Async operation ID.
+ protected long QueueRenderTask()
+ {
+ long id = nextID++;
+ EnqueueTask(id);
+ return id;
+ }
+
+ private void OnTaskCompleted(RenderResult r, RenderTaskState state)
+ {
+ if (r != null && !state.IsCanceled)
+ {
+
+ WriteableBitmap wr = new WriteableBitmap((int)r.Output.Width, (int)r.Output.Height, 96, 96, PixelFormats.Bgra32, null);
+ // Calculate the number of bytes per pixel.
+ int bytesPerPixel = (wr.Format.BitsPerPixel + 7) / 8;
+ // Stride is bytes per pixel times the number of pixels.
+ // Stride is the byte width of a single rectangle row.
+ int stride = wr.PixelWidth * bytesPerPixel;
+ wr.WritePixels(new Int32Rect(0, 0, wr.PixelWidth, wr.PixelHeight), r.Image, stride, 0);
+
+
+ outputImage.Source = wr;
+ Canvas.SetLeft(outputImage, r.Output.Left);
+ Canvas.SetTop(outputImage, r.Output.Top);
+ imageCartesianRect = r.Visible;
+ imageSize = new Size(r.Output.Width, r.Output.Height);
+ outputImage.RenderTransform = null;
+ }
+
+ RaiseTaskCompletion(state.Id);
+
+ runningTasks.Remove(state);
+
+ while (tasks.Count > 1)
+ {
+ long id = tasks.Dequeue();
+ RaiseTaskCompletion(id);
+ }
+ if (tasks.Count > 0 && runningTasks.Count < maxTasks)
+ {
+ EnqueueTask(tasks.Dequeue());
+ }
+
+ InvalidateMeasure();
+ }
+
+ ///
+ /// Cancel all tasks, which are in quere.
+ ///
+ public void CancelAll()
+ {
+ foreach (var s in runningTasks)
+ s.Stop();
+ }
+
+ ///
+ /// Gets event which is occured when render task is finished
+ ///
+ public IObservable RenderCompletion
+ {
+ get { return null; }
+ }
+
+ ///
+ /// Raises RenderCompletion event when task with the specified id is finished
+ ///
+ /// ID of finished task
+ protected void RaiseTaskCompletion(long id)
+ {
+ //renderCompletion.OnNext(new RenderCompletion { TaskId = id });
+ }
+ }
+
+ ///
+ /// Represents contents of render result.
+ ///
+ public class RenderResult
+ {
+ private DataRect visible;
+ private Rect output;
+ private int[] image;
+
+ ///
+ /// Initializes new instance of RenderResult class form given parameters.
+ ///
+ /// Array of image pixels.
+ /// Visible rect for graph.
+ /// Image start offset.
+ /// Image width.
+ /// image height.
+ public RenderResult(int[] image, DataRect visible, Point offset, double width, double height)
+ {
+ this.visible = visible;
+ this.output = new Rect(offset, new Size(width, height));
+ this.image = image;
+ }
+
+ ///
+ /// Gets the current visible rect.
+ ///
+ public DataRect Visible
+ {
+ get { return visible; }
+ }
+
+ ///
+ /// Gets an array of image pixels.
+ ///
+ public int[] Image
+ {
+ get { return image; }
+ }
+
+ ///
+ /// Gets the image output rect.
+ ///
+ public Rect Output
+ {
+ get { return output; }
+ }
+ }
+
+ /// This class holds all information about rendering request.
+ public class RenderTaskState
+ {
+ bool isCanceled = false;
+
+ ///
+ /// Initializes new instance of RenderTaskState class from given coordinate tranform.
+ ///
+ public RenderTaskState(DataRect actualPlotRect, Size screenSize)
+ {
+ ScreenSize = screenSize;
+ ActualPlotRect = actualPlotRect;
+ }
+
+ ///
+ /// Gets or sets the state Id.
+ ///
+ public long Id { get; set; }
+
+ ///
+ /// Gets the screen size of the output image
+ ///
+ public Size ScreenSize { get; private set; }
+
+ ///
+ /// Gets plot rectangle of the visible area
+ ///
+ public DataRect ActualPlotRect { get; private set; }
+
+ ///
+ /// Gets or sets the current bounds.
+ ///
+ public DataRect Bounds { get; set; }
+
+ ///
+ /// Gets a value indicating whether the task is cancelled or not.
+ ///
+ public bool IsCanceled
+ {
+ get { return isCanceled; }
+ }
+
+ ///
+ /// Sets state as canceled.
+ ///
+ public void Stop()
+ {
+ isCanceled = true;
+ }
+ }
+
+ ///
+ ///Contains reference to completed tasks.
+ ///
+ public class RenderCompletion
+ {
+ ///
+ ///Gets or sets the Id of render task.
+ ///
+ public long TaskId { get; set; }
+ }
+
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Plots/Base/Plot.cs b/InteractiveDataDisplay_OneDll/src/Plots/Base/Plot.cs
new file mode 100644
index 000000000..84c696851
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Plots/Base/Plot.cs
@@ -0,0 +1,485 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Shapes;
+using System.ComponentModel;
+
+namespace InteractiveDataDisplay.WPF {
+ ///
+ /// Plot is a panel that supports positioning of child elements in plot coordinates by
+ /// providing attached properties X1,Y1,X2,Y2 and Points.
+ /// Plot panel automatically computes bounding rectangle for all child elements.
+ ///
+ [Description("Position child elements in plot coordinates")]
+ public class Plot : PlotBase
+ {
+ #region Attached Properties
+
+ ///
+ /// Identifies the Plot.X1 attached property.
+ ///
+ public static readonly DependencyProperty X1Property =
+ DependencyProperty.RegisterAttached("X1", typeof(double), typeof(Plot), new PropertyMetadata(double.NaN, DataCoordinatePropertyChangedHandler));
+
+ ///
+ /// Sets the value of the Plot.X1 attached property for a given dependency object.
+ ///
+ /// The element to which the property value is written
+ /// Sets the Plot.X1 coordinate of the specified element
+ ///
+ /// Infinity values for Plot.X1 are handled is special way.
+ /// They do not participate in plot bounds computation,
+ /// “+Infinity” is translated to maximum visible value, “-Infinity” is translated to minimum visible coordinate.
+ ///
+ public static void SetX1(DependencyObject element, double value)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+
+ element.SetValue(X1Property, value);
+ }
+
+ ///
+ /// Returns the value of the Plot.X1 attached property for a given dependency object.
+ ///
+ /// The element from which the property value is read
+ /// The Plot.X1 coordinate of the specified element
+ ///
+ /// Default value of Plot.X1 property is Double.NaN. Element is horizontally arranged inside panel according
+ /// to values of X1 and X2 attached property. X1 and X2 doesn't have to be ordered. If X1 is not
+ /// specified (has NaN value) than Canvas.Left value is used.
+ ///
+ public static double GetX1(DependencyObject element)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+
+ return (double)element.GetValue(X1Property);
+ }
+
+ ///
+ /// Identifies the Plot.X2 attached property.
+ ///
+ public static readonly DependencyProperty X2Property =
+ DependencyProperty.RegisterAttached("X2", typeof(double), typeof(Plot), new PropertyMetadata(double.NaN, DataCoordinatePropertyChangedHandler));
+
+ ///
+ /// Sets the value of the Plot.X2 attached property for a given dependency object.
+ ///
+ /// The element to which the property value is written
+ /// Sets the Plot.X2 coordinate of the specified element
+ ///
+ /// Infinity values for Plot.X2 are handled is special way.
+ /// They do not participate in plot bounds computation,
+ /// “+Infinity” is translated to maximum visible value, “-Infinity” is translated to minimum visible coordinate.
+ ///
+ public static void SetX2(DependencyObject element, double value)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+
+ element.SetValue(X2Property, value);
+ }
+
+ ///
+ /// Returns the value of the Plot.X2 attached property for a given dependency object.
+ ///
+ /// The element from which the property value is read
+ /// The Plot.X2 coordinate of the specified element
+ ///
+ /// Default value of Plot.X2 property is Double.NaN.
+ /// Element is horizontally arranged inside panel according
+ /// to values of X1 and X2 attached property. X1 and X2 doesn't have to be ordered. If X2
+ /// is not specified (has NaN value) then Width property is used to define element arrangement.
+ ///
+ public static double GetX2(DependencyObject element)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+
+ return (double)element.GetValue(X2Property);
+ }
+
+ ///
+ /// Identifies the Plot.Y1 attached property.
+ ///
+ public static readonly DependencyProperty Y1Property =
+ DependencyProperty.RegisterAttached("Y1", typeof(double), typeof(Plot), new PropertyMetadata(double.NaN, DataCoordinatePropertyChangedHandler));
+
+ ///
+ /// Sets the value of the Plot.Y1 attached property for a given dependency object.
+ ///
+ /// The element to which the property value is written
+ /// Sets the Plot.Y1 coordinate of the specified element
+ ///
+ /// Infinity values for Plot.Y1 are handled is special way.
+ /// They do not participate in plot bounds computation,
+ /// “+Infinity” is translated to maximum visible value, “-Infinity” is translated to minimum visible coordinate.
+ ///
+ public static void SetY1(DependencyObject element, double value)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+
+ element.SetValue(Y1Property, value);
+ }
+
+ ///
+ /// Returns the value of the Plot.Y1 attached property for a given dependency object.
+ ///
+ /// The element from which the property value is read
+ /// The Plot.Y1 coordinate of the specified element
+ ///
+ /// Default value of Plot.Y1 property is Double.NaN. Element is vertically arranged inside panel according
+ /// to values of Y1 and Y2 attached property. Y1 and Y2 doesn't have to be ordered.
+ /// If Y1 is not specifed (has NaN value) then Canvas.Top property is used to define element position.
+ ///
+ public static double GetY1(DependencyObject element)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+
+ return (double)element.GetValue(Y1Property);
+ }
+
+ ///
+ /// Identifies the Plot.Y2 attached property.
+ ///
+ public static readonly DependencyProperty Y2Property =
+ DependencyProperty.RegisterAttached("Y2", typeof(double), typeof(Plot), new PropertyMetadata(double.NaN, DataCoordinatePropertyChangedHandler));
+
+ ///
+ /// Sets the value of the Plot.Y2 attached property for a given dependency object.
+ ///
+ /// The element to which the property value is written
+ /// Sets the Plot.Y2 coordinate of the specified element
+ ///
+ /// Infinity values for Plot.Y2 are handled is special way.
+ /// They do not participate in plot bounds computation,
+ /// “+Infinity” is translated to maximum visible value, “-Infinity” is translated to minimum visible coordinate.
+ ///
+ public static void SetY2(DependencyObject element, double value)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+
+ element.SetValue(Y2Property, value);
+ }
+
+ ///
+ /// Returns the value of the Plot.Y2 attached property for a given dependency object.
+ ///
+ /// The element from which the property value is read
+ /// The Plot.Y2 coordinate of the specified element
+ ///
+ /// Default value of Plot.Y2 property is Double.NaN. Element is vertically arranged inside panel according
+ /// to values of Y1 and Y2 attached property. Y1 and Y2 doesn't have to be ordered.
+ /// If Y2 is not specifed (has NaN value) then Height property is used to define element arrangement.
+ ///
+ public static double GetY2(DependencyObject element)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+
+ return (double)element.GetValue(Y2Property);
+ }
+
+ ///
+ /// Identifies the Plot.Points attached property.
+ ///
+ public static readonly DependencyProperty PointsProperty =
+ DependencyProperty.RegisterAttached("Points", typeof(PointCollection), typeof(Plot), new PropertyMetadata(null, DataCoordinatePropertyChangedHandler));
+
+ ///
+ /// Sets the value of the Plot.Points attached property for a given dependency object.
+ ///
+ /// The element to which the property value is written
+ /// Sets the Plot.Points coollection of the specified element
+ public static void SetPoints(DependencyObject element, PointCollection value)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+
+ element.SetValue(PointsProperty, value);
+ }
+
+ ///
+ /// Returns the value of the Plot.Points attached property for a given dependency object.
+ ///
+ /// The element from which the property value is read
+ /// The Plot.Points collection of the specified element
+ ///
+ /// Default value of Plot.Points property is null
+ ///
+ public static PointCollection GetPoints(DependencyObject element)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+
+ return (PointCollection)element.GetValue(PointsProperty);
+ }
+
+ static void DataCoordinatePropertyChangedHandler(DependencyObject obj, DependencyPropertyChangedEventArgs args)
+ {
+ var parent = VisualTreeHelper.GetParent(obj);
+ if (parent is ContentPresenter) parent = VisualTreeHelper.GetParent(parent);
+ var plotBase = parent as PlotBase;
+ if (plotBase != null)
+ {
+ plotBase.InvalidateBounds();
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Computes minimal plot rectangle, which contains all plot rectangles of child elements
+ ///
+ /// Minimal plot rectangle, which contains all plot rectangles of child elements
+ protected override DataRect ComputeBounds()
+ {
+ var localPlotRect = DataRect.Empty;
+ foreach (UIElement child in Children)
+ {
+ DependencyObject item = child ;
+ if (item is ContentPresenter && VisualTreeHelper.GetChildrenCount(item) == 1)
+ item = VisualTreeHelper.GetChild(item, 0);
+ double v;
+ v = GetX1(item);
+ if (!double.IsNaN(v) && !double.IsInfinity(v)) localPlotRect.XSurround(XDataTransform.DataToPlot(v));
+ v = GetX2(item);
+ if (!double.IsNaN(v) && !double.IsInfinity(v)) localPlotRect.XSurround(XDataTransform.DataToPlot(v));
+ v = GetY1(item);
+ if (!double.IsNaN(v) && !double.IsInfinity(v)) localPlotRect.YSurround(YDataTransform.DataToPlot(v));
+ v = GetY2(item);
+ if (!double.IsNaN(v) && !double.IsInfinity(v)) localPlotRect.YSurround(YDataTransform.DataToPlot(v));
+ var points = GetPoints(item);
+ if (points != null)
+ foreach (var point in points)
+ {
+ localPlotRect.XSurround(XDataTransform.DataToPlot(point.X));
+ localPlotRect.YSurround(YDataTransform.DataToPlot(point.Y));
+ }
+ }
+ return localPlotRect;
+ }
+
+ ///
+ /// Positions child elements and determines a size for a Plot
+ ///
+ /// The final area within the parent that Plot should use to arrange itself and its children
+ /// The actual size used
+ protected override Size ArrangeOverride(Size finalSize)
+ {
+ ArrangeChildren(finalSize);
+
+ if (ClipToBounds)
+ Clip = new RectangleGeometry { Rect = new Rect(new Point(0, 0), finalSize) };
+ else
+ Clip = null;
+
+ return finalSize;
+ }
+
+ ///
+ /// Measures the size in layout required for child elements and determines a size for the Plot.
+ ///
+ /// The available size that this element can give to child elements. Infinity can be specified as a value to indicate that the element will size to whatever content is available.
+ /// The size that this element determines it needs during layout, based on its calculations of child element sizes.
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ availableSize = PerformAsMaster(availableSize);
+ MeasureChildren(availableSize);
+ return availableSize;
+ }
+
+ internal void MeasureChildren(Size availableSize)
+ {
+ foreach (UIElement child in Children)
+ {
+ DependencyObject item = child;
+ if (item is ContentPresenter && VisualTreeHelper.GetChildrenCount(item) == 1)
+ item = VisualTreeHelper.GetChild(item, 0);
+
+ var xy = GetPoints(item);
+ if (xy != null)
+ if (item is Polyline)
+ {
+ var line = (Polyline)item;
+ var points = new PointCollection();
+ foreach (var point in xy) points.Add(new Point(LeftFromX(XDataTransform.DataToPlot(point.X)), TopFromY(YDataTransform.DataToPlot(point.Y))));
+ line.Points = points;
+ }
+ else if (item is Polygon)
+ {
+ var p = (Polygon)item;
+ var points = new PointCollection();
+ foreach (var point in xy) points.Add(new Point(LeftFromX(XDataTransform.DataToPlot(point.X)), TopFromY(YDataTransform.DataToPlot(point.Y))));
+ p.Points = points;
+ }
+ if (item is Line)
+ {
+ var line = (Line)item;
+ double v;
+ v = GetX1(line);
+ if (!double.IsNaN(v))
+ {
+ if (Double.IsNegativeInfinity(v))
+ line.X1 = 0;
+ else if (Double.IsPositiveInfinity(v))
+ line.X1 = availableSize.Width;
+ else
+ line.X1 = LeftFromX(XDataTransform.DataToPlot(v));
+ }
+ v = GetX2(line);
+ if (!double.IsNaN(v))
+ {
+ if (Double.IsNegativeInfinity(v))
+ line.X2 = 0;
+ else if (Double.IsPositiveInfinity(v))
+ line.X2 = availableSize.Width;
+ else
+ line.X2 = LeftFromX(XDataTransform.DataToPlot(v));
+ }
+ v = GetY1(line);
+ if (!double.IsNaN(v))
+ {
+ if (Double.IsNegativeInfinity(v))
+ line.Y1 = availableSize.Height;
+ else if (Double.IsPositiveInfinity(v))
+ line.Y1 = 0;
+ else
+ line.Y1 = TopFromY(YDataTransform.DataToPlot(v));
+ }
+ v = GetY2(line);
+ if (!double.IsNaN(v))
+ {
+ if (Double.IsNegativeInfinity(v))
+ line.Y2 = availableSize.Height;
+ else if (Double.IsPositiveInfinity(v))
+ line.Y2 = 0;
+ else
+ line.Y2 = TopFromY(YDataTransform.DataToPlot(v));
+ }
+ }
+ child.Measure(availableSize);
+ }
+ }
+
+ internal void ArrangeChildren(Size finalSize)
+ {
+ foreach (UIElement child in Children)
+ {
+ DependencyObject item = child;
+ if (item is ContentPresenter && VisualTreeHelper.GetChildrenCount(item) == 1)
+ item = VisualTreeHelper.GetChild(item, 0);
+
+ if (item is Line || item is Polyline)
+ {
+ child.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
+ }
+ else
+ {
+ double x1 = GetX1(item);
+ double x2 = GetX2(item);
+ double y1 = GetY1(item);
+ double y2 = GetY2(item);
+
+ Size desiredSize = Size.Empty;
+ if (double.IsNaN(x1) || double.IsNaN(x2) || double.IsNaN(y1) || double.IsNaN(y2))
+ desiredSize = child.DesiredSize;
+
+ double L = 0.0;
+ if (!double.IsNaN(x1))
+ {
+ if (double.IsNegativeInfinity(x1))
+ L = 0;
+ else if (double.IsPositiveInfinity(x1))
+ L = finalSize.Width;
+ else
+ L = LeftFromX(XDataTransform.DataToPlot(x1)); // x1 is not Nan and Inf here
+ }
+ else
+ L = (double)item.GetValue(Canvas.LeftProperty);
+
+ double W = 0.0;
+ var elem = item as FrameworkElement;
+ if (!double.IsNaN(x1) && !double.IsNaN(x2))
+ {
+ double L2 = 0.0;
+ if (double.IsNegativeInfinity(x2))
+ L2 = 0;
+ else if (double.IsPositiveInfinity(x2))
+ L2 = desiredSize.Width;
+ else
+ L2 = LeftFromX(XDataTransform.DataToPlot(x2)); // x2 is not Nan and Inf here
+
+ if (L2 >= L) W = L2 - L;
+ else
+ {
+ W = L - L2;
+ L = L2;
+ }
+ }
+ else if (elem != null || double.IsNaN(W = elem.Width) || double.IsInfinity(W))
+ W = desiredSize.Width;
+
+ double T = 0.0;
+ if (!double.IsNaN(y1))
+ {
+ if (double.IsNegativeInfinity(y1))
+ T = desiredSize.Height;
+ else if (double.IsPositiveInfinity(y1))
+ T = 0;
+ else
+ T = TopFromY(YDataTransform.DataToPlot(y1)); // y1 is not Nan and Inf here
+ }
+ else
+ T = (double)item.GetValue(Canvas.TopProperty);
+
+ double H = 0.0;
+ if (!double.IsNaN(y1) && !double.IsNaN(y2))
+ {
+ double T2 = 0.0;
+ if (double.IsNegativeInfinity(y2))
+ T2 = desiredSize.Height;
+ else if (double.IsPositiveInfinity(y2))
+ T2 = desiredSize.Width;
+ else
+ T2 = TopFromY(YDataTransform.DataToPlot(y2)); // y2 is not Nan and Inf here
+ if (T2 >= T) H = T2 - T;
+ else
+ {
+ H = T - T2;
+ T = T2;
+ }
+ }
+ else if (elem != null || double.IsNaN(H = elem.Height) || double.IsInfinity(H))
+ H = desiredSize.Height;
+
+ if (Double.IsNaN(L) || Double.IsInfinity(L) || Double.IsNaN(W) || Double.IsInfinity(W)) // Horizontal data to screen transform fails
+ {
+ L = 0;
+ W = desiredSize.Width;
+ }
+ if (Double.IsNaN(T) || Double.IsInfinity(T) || Double.IsNaN(H) || Double.IsInfinity(H)) // Vertical data to screen transform fails
+ {
+ T = 0;
+ H = desiredSize.Height;
+ }
+ child.Arrange(new Rect(L, T, W, H));
+ }
+ }
+ }
+
+ #endregion
+
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Plots/Heatmap/HeatmapBuilder.cs b/InteractiveDataDisplay_OneDll/src/Plots/Heatmap/HeatmapBuilder.cs
new file mode 100644
index 000000000..eca47d58b
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Plots/Heatmap/HeatmapBuilder.cs
@@ -0,0 +1,480 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Windows;
+using System.Windows.Media;
+using System.Collections.Generic;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Helper class, which provides various methods for building heatmaps.
+ ///
+ public static class HeatmapBuilder
+ {
+ ///
+ /// Builds heatmap from specified data, grid, missing value, visibleRect and palette.
+ ///
+ /// 2D array with data to plot
+ /// Missing value. Heatmap will have transparent regions at missing value.
+ /// Palette to translate numeric values to colors
+ /// Min and max values in array
+ /// Heatmap rectangle screen coordinates
+ /// Plot rectaneg screen coordinates
+ static public int[] BuildHeatMap(Rect screen, DataRect rect, double[] x, double[] y, double[,] data,
+ double missingValue, IPalette palette, Range range)
+ {
+ if (data == null)
+ throw new ArgumentNullException("data");
+ if (x == null)
+ throw new ArgumentNullException("x");
+ if (y == null)
+ throw new ArgumentNullException("y");
+ if (palette == null)
+ throw new ArgumentNullException("palette");
+
+ int pixelWidth = (int)screen.Width;
+ int pixelHeight = (int)screen.Height;
+ int[] pixels = new int[pixelWidth * pixelHeight];
+ double doubleMissingValue = missingValue;
+ UInt32[] paletteColors = new uint[512];
+
+ if (!palette.IsNormalized)
+ range = palette.Range;
+
+ for (int i = 0; i < 512; i++)
+ {
+ Color c;
+ if (palette.IsNormalized)
+ c = palette.GetColor((double)i / 511.0);
+ else
+ c = palette.GetColor(range.Min + i * (range.Max - range.Min) / 511.0);
+
+ paletteColors[i] = (((uint)(c.A)) << 24) | (((uint)c.R) << 16) | (((uint)c.G) << 8) | c.B;
+ }
+
+ int xdimRank = x.Length;
+ int ydimRank = y.Length;
+
+ int xdataRank = data.GetLength(0);
+ int ydataRank = data.GetLength(1);
+
+ double factor = (range.Max != range.Min) ? (1.0 / (range.Max - range.Min)) : 0.0;
+
+ if (xdataRank == xdimRank && ydataRank == ydimRank)
+ {
+ double[,] v = null;
+ v = Filter(pixelWidth, pixelHeight, rect, x, y, data, doubleMissingValue);
+ if (palette.IsNormalized)
+ {
+ if (Double.IsNaN(doubleMissingValue))
+ {
+ for (int j = 0, k = 0; j < pixelHeight; j++)
+ {
+ for (int i = 0; i < pixelWidth; i++, k++)
+ {
+ double vv = v[i, pixelHeight - j - 1];
+ if (!Double.IsNaN(vv))
+ {
+ double v01 = (vv - range.Min) * factor;
+ pixels[k] = (int)paletteColors[((uint)(511 * v01)) & 0x1FF];
+ }
+ }
+ }
+ }
+ else
+ {
+ for (int j = 0, k = 0; j < pixelHeight; j++)
+ {
+ for (int i = 0; i < pixelWidth; i++, k++)
+ {
+ double vv = v[i, pixelHeight - j - 1];
+ if (vv != doubleMissingValue)
+ {
+ double v01 = (vv - range.Min) * factor;
+ pixels[k] = (int)paletteColors[((uint)(511 * v01)) & 0x1FF];
+ }
+ }
+ }
+ }
+ }
+ else // Palette is absolute
+ {
+ if (Double.IsNaN(doubleMissingValue))
+ {
+ for (int j = 0, k = 0; j < pixelHeight; j++)
+ {
+ for (int i = 0; i < pixelWidth; i++, k++)
+ {
+ double vv = v[i, pixelHeight - j - 1];
+ if (!Double.IsNaN(vv))
+ {
+ double v01 = (Math.Max(range.Min, Math.Min(range.Max, vv)) - range.Min) * factor;
+ pixels[k] = (int)paletteColors[((uint)(511 * v01)) & 0x1FF];
+ }
+ }
+ }
+ }
+ else
+ {
+ for (int j = 0, k = 0; j < pixelHeight; j++)
+ {
+ for (int i = 0; i < pixelWidth; i++, k++)
+ {
+ double vv = v[i, pixelHeight - j - 1];
+ if (vv != doubleMissingValue)
+ {
+ double v01 = (Math.Max(range.Min, Math.Min(range.Max, vv)) - range.Min) * factor;
+ pixels[k] = (int)paletteColors[((uint)(511 * v01)) & 0x1FF];
+ }
+ }
+ }
+ }
+ }
+ }
+ else if ((xdataRank + 1) == xdimRank && (ydataRank + 1) == ydimRank)
+ {
+
+ // Prepare arrays
+ int[] xPixelDistrib;
+ int[] yPixelDistrib;
+
+ xPixelDistrib = CreatePixelToDataMap(pixelWidth, Math.Max(x[0], rect.XMin), Math.Min(x[x.Length - 1], rect.XMax), x);
+ yPixelDistrib = CreatePixelToDataMap(pixelHeight, Math.Max(y[0], rect.YMin), Math.Min(y[y.Length - 1], rect.YMax), y);
+
+ if (palette.IsNormalized)
+ {
+ if (Double.IsNaN(doubleMissingValue))
+ {
+ for (int j = 0, k = 0; j < pixelHeight; j++)
+ {
+ for (int i = 0; i < pixelWidth; i++, k++)
+ {
+ double vv = data[xPixelDistrib[i], yPixelDistrib[pixelHeight - j - 1]];
+ if (!Double.IsNaN(vv))
+ {
+ double v01 = (vv - range.Min) * factor;
+ pixels[k] = (int)paletteColors[((uint)(511 * v01)) & 0x1FF];
+ }
+ }
+ }
+ }
+ else
+ {
+ for (int j = 0, k = 0; j < pixelHeight; j++)
+ {
+ for (int i = 0; i < pixelWidth; i++, k++)
+ {
+ double vv = data[xPixelDistrib[i], yPixelDistrib[pixelHeight - j - 1]];
+ if (vv != doubleMissingValue)
+ {
+ double v01 = (vv - range.Min) * factor;
+ pixels[k] = (int)paletteColors[((uint)(511 * v01)) & 0x1FF];
+ }
+ }
+ }
+ }
+ }
+ else // Palette is absolute
+ {
+ if (Double.IsNaN(doubleMissingValue))
+ {
+ for (int j = 0, k = 0; j < pixelHeight; j++)
+ {
+ for (int i = 0; i < pixelWidth; i++, k++)
+ {
+ double vv = data[xPixelDistrib[i], yPixelDistrib[pixelHeight - j - 1]];
+ if (!Double.IsNaN(vv))
+ {
+ double v01 = (Math.Max(range.Min, Math.Min(range.Max, vv)) - range.Min) * factor;
+ pixels[k] = (int)paletteColors[((uint)(511 * v01)) & 0x1FF];
+ }
+ }
+ }
+ }
+ else
+ {
+ for (int j = 0, k = 0; j < pixelHeight; j++)
+ {
+ for (int i = 0; i < pixelWidth; i++, k++)
+ {
+ double vv = data[xPixelDistrib[i], yPixelDistrib[pixelHeight - j - 1]];
+ if (vv != doubleMissingValue)
+ {
+ double v01 = (Math.Max(range.Min, Math.Min(range.Max, vv)) - range.Min) * factor;
+ pixels[k] = (int)paletteColors[((uint)(511 * v01)) & 0x1FF];
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ throw new ArgumentException("Size of x,y and data arrays does not match conditions");
+ }
+
+ return pixels;
+ }
+
+
+ /// Prepares linear interpolation filter tables
+ /// Size of heatmap in pixels
+ /// Plot coordinate corresponding to left pixel
+ /// Plot coordinate corresponding to right pixel
+ /// Plot coordinates of data points
+ /// Indices in target array (pixels)
+ /// Indices in source array
+ /// Linear transform coefficient
+ /// Weights
+ ///
+ ///
+ /// Following code performs linear interpolation without taking missing values into account.
+ /// Names of variables correspond to parameter names
+ ///
+ ///
+ ///
+ ///
+ public static void PrepareFilterTables(int width, double minX, double maxX, double[] x,
+ out int[] pixels, out int[] indexes, out double[] alphas, out double[] weights)
+ {
+ if (x == null)
+ throw new ArgumentNullException("x");
+
+ int length = x.Length;
+ if (length <= 1)
+ throw new ArgumentException("Knots array should contain at least 2 knots");
+ if (maxX > x[length - 1])
+ maxX = x[length - 1];
+ else if (minX < x[0])
+ minX = x[0];
+
+ List p = new List(length);
+ List idx = new List(length);
+ List k = new List(length);
+ List w = new List(length);
+
+
+ int i = 0;
+ double delta = (maxX - minX) / width;
+ for (int pixel = 0; pixel < width; pixel++)
+ {
+ double pixmin = minX + pixel * delta; // Current pixel is segment [pixmin, pixmin + delta]
+ while (x[i + 1] <= pixmin)
+ i++; // No out of range here because of prerequisites
+ while (i < length - 1)
+ {
+ double min = Math.Max(pixmin, x[i]);
+ double max = Math.Min(pixmin + delta, x[i + 1]);
+ double center = (min + max) / 2;
+ idx.Add(i);
+ p.Add(pixel);
+ k.Add((x[i + 1] - center) / (x[i + 1] - x[i]));
+ w.Add((max - min) / delta);
+ if (x[i + 1] >= pixmin + delta)
+ break;
+ else
+ i++;
+ }
+ }
+ pixels = p.ToArray();
+ indexes = idx.ToArray();
+ alphas = k.ToArray();
+ weights = w.ToArray();
+ }
+
+ /// Performs array resize and subset with linear interpolation
+ /// Result width
+ /// Result height
+ /// Range of coordinates for entire data
+ /// Coordinates of x points (length of array must be
+ /// Coordinates of y points (length of array must be
+ /// Source data array
+ /// Missing value or NaN if no missing value
+ /// Resulting array with size *
+ public static double[,] Filter(int width, int height, DataRect rect,
+ double[] x, double[] y, double[,] data, double missingValue)
+ {
+ if (data == null)
+ throw new ArgumentNullException("data");
+ if (x == null)
+ throw new ArgumentNullException("x");
+ if (y == null)
+ throw new ArgumentNullException("y");
+
+ // Check preconditions
+ if (x.Length != data.GetLength(0))
+ throw new ArgumentException("Size of x and data arrays does not match");
+ if (y.Length != data.GetLength(1))
+ throw new ArgumentException("Size of y and data arrays does not match");
+
+ // Prepare filters
+ int[] hp, vp, hi, vi;
+ double[] ha, va, hw, vw;
+ PrepareFilterTables(width, rect.XMin, rect.XMax, x, out hp, out hi, out ha, out hw);
+ PrepareFilterTables(height, rect.YMin, rect.YMax, y, out vp, out vi, out va, out vw);
+
+ // Prepare arrays
+ double[,] r = new double[width, height];
+ bool hasMissingValue = !Double.IsNaN(missingValue);
+ double offset = 0;
+ if (hasMissingValue && missingValue == 0)
+ {
+ offset = -1;
+ for (int i = 0; i < width; i++)
+ for (int j = 0; j < height; j++)
+ r[i, j] = offset;
+ }
+
+ // Do filtering
+ int hpLen = hp.Length;
+ int vpLen = vp.Length;
+ for (int i = 0; i < hpLen; i++)
+ {
+ int px = hp[i];
+ int i0 = hi[i];
+
+ for (int j = 0; j < vpLen; j++)
+ {
+ int py = vp[j];
+ if (hasMissingValue && r[px, py] == missingValue)
+ continue;
+ int j0 = vi[j];
+ if (hasMissingValue &&
+ (data[i0, j0] == missingValue ||
+ data[i0 + 1, j0] == missingValue ||
+ data[i0, j0 + 1] == missingValue ||
+ data[i0 + 1, j0 + 1] == missingValue))
+ r[px, py] = missingValue;
+ else
+ {
+ double v0 = ha[i] * data[i0, j0] + (1 - ha[i]) * data[i0 + 1, j0];
+ double v1 = ha[i] * data[i0, j0 + 1] + (1 - ha[i]) * data[i0 + 1, j0 + 1];
+ double v = va[j] * v0 + (1 - va[j]) * v1;
+ r[px, py] += v * hw[i] * vw[j];
+ }
+ }
+ }
+ // Offset data if needed
+ if (offset != 0)
+ for (int i = 0; i < width; i++)
+ for (int j = 0; j < height; j++)
+ if (r[i, j] != missingValue)
+ r[i, j] -= offset;
+
+ return r;
+ }
+
+ /// Computes array of indices in data array for rendering heatmap with given width in colormap mode
+ /// Width of heatmap in pixels
+ /// Data value corresponding to left heatmap edge
+ /// Data value corresponding to right heatmap edge
+ /// Grid array. Note that and should be
+ /// inside grid range. Otherwise exception will occur.
+ /// Array of length where value of each element is index in data
+ /// array
+ private static int[] CreatePixelToDataMap(int width, double xmin, double xmax, double[] x)
+ {
+ int length = x.Length;
+ if (length <= 1)
+ throw new ArgumentException("Knots array should contain at least 2 knots");
+ if (xmax > x[length - 1] || xmin < x[0])
+ throw new ArgumentException("Cannot interpolate beyond bounds");
+
+ int[] pixels = new int[width];
+ double delta = (xmax - xmin) / width;
+ int i = 0;
+ for (int pixel = 0; pixel < width; pixel++)
+ {
+ double pixCenter = xmin + (pixel + 0.5) * delta; // Pixel center
+ while (x[i+1] < pixCenter)
+ i++; // No out of range here because of prerequisites. Now x[i+1] > pixCenter
+ pixels[pixel] = i;
+ }
+
+ return pixels;
+ }
+
+ ///
+ /// Calculates data range from specified 2d array of data.
+ ///
+ public static Range GetMaxMin(double[,] data)
+ {
+ double Max, Min;
+ if (data != null)
+ {
+ int N = data.GetLength(0);
+ int M = data.GetLength(1);
+ Max = data[0, 0];
+ Min = data[0, 0];
+
+ for (int i = 0; i < N; i++)
+ for (int j = 0; j < M; j++)
+ {
+ if (!double.IsNaN(data[i, j]))
+ {
+ if (data[i, j] > Max) Max = data[i, j];
+ if (data[i, j] < Min) Min = data[i, j];
+ }
+ }
+ if (double.IsNaN(Max) || double.IsNaN(Min))
+ {
+ Max = 0;
+ Min = 0;
+ }
+ }
+ else
+ {
+ Max = Double.NaN;
+ Min = Double.NaN;
+ }
+ return new Range(Min, Max); ;
+ }
+
+ ///
+ /// Calculates data range from specified 2d array of data and missing value.
+ ///
+ public static Range GetMaxMin(double[,] data, double missingValue)
+ {
+ double Max, Min;
+ if (data != null)
+ {
+ int N = data.GetLength(0);
+ int M = data.GetLength(1);
+
+ Max = data[0, 0];
+ Min = data[0, 0];
+
+ for (int i = 0; i < N; i++)
+ for (int j = 0; j < M; j++)
+ {
+ if (data[i, j] != missingValue)
+ {
+ if (data[i, j] > Max) Max = data[i, j];
+ if (data[i, j] < Min) Min = data[i, j];
+ }
+ }
+ if (Max == missingValue || Min == missingValue)
+ {
+ Max = 0;
+ Min = 0;
+ }
+ }
+ else
+ {
+ Max = missingValue;
+ Min = missingValue;
+ }
+ return new Range(Min, Max); ;
+ }
+
+ }
+
+
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Plots/Heatmap/HeatmapGraph.cs b/InteractiveDataDisplay_OneDll/src/Plots/Heatmap/HeatmapGraph.cs
new file mode 100644
index 000000000..def777a0e
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Plots/Heatmap/HeatmapGraph.cs
@@ -0,0 +1,450 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Windows;
+using System.ComponentModel;
+using System.Globalization;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Control to render 2d image as a heatmap. Heatmap is widely used graphical representation of 2D array
+ /// where the values of array items are represented as colors.
+ ///
+ [Description("Plots a heatmap graph")]
+ public class HeatmapGraph : BackgroundBitmapRenderer, ITooltipProvider
+ {
+ private object locker = new object(); // Instance to hold locks for multithread access
+
+ private double[] xArr;
+ private double[] yArr;
+ private long dataVersion = 0;
+ private double[,] data;
+ private double missingValue;
+
+ ///
+ /// Initializes a new instance of class with default tooltip.
+ ///
+ public HeatmapGraph()
+ {
+ TooltipContentFunc = GetTooltipForPoint;
+ }
+
+ private static void VerifyDimensions(double[,] d, double[] x, double[] y)
+ {
+ double dlen0 = d.GetLength(0);
+ double dlen1 = d.GetLength(1);
+ double xlen = x.Length;
+ double ylen = y.Length;
+ if (dlen0 == xlen && dlen1 == ylen ||
+ dlen0 == xlen - 1 && xlen > 1 && dlen1 == ylen - 1 && ylen > 1)
+ return;
+ throw new ArgumentException("Array dimensions do not match");
+ }
+
+ /// Plots rectangular heatmap.
+ /// If size dimensions are equal to lenghtes of corresponding grid parameters
+ /// and then Gradient render method is used. If
+ /// dimension are smaller by one then Bitmap render method is used for heatmap. In all other cases exception is thrown.
+ ///
+ /// Two dimensional array of data.
+ /// Grid along x axis.
+ /// Grid along y axis.
+ /// ID of background operation. You can subscribe to
+ /// notification to be notified when this operation is completed or request is dropped.
+ public long Plot(double[,] data, double[] x, double[] y)
+ {
+ return Plot(data, x, y, Double.NaN);
+ }
+
+ /// Plots rectangular heatmap where some data may be missing.
+ /// If size dimensions are equal to lenghtes of corresponding grid parameters
+ /// and then Gradient render method is used. If
+ /// dimension are smaller by one then Bitmap render method is used for heatmap. In all other cases exception is thrown.
+ ///
+ /// Two dimensional array of data.
+ /// Grid along x axis.
+ /// Grid along y axis.
+ /// Missing value. Data items equal to aren't shown.
+ /// ID of background operation. You can subscribe to
+ /// notification to be notified when this operation is completed or request is dropped.
+ public long Plot(double[,] data, double[] x, double[] y, double missingValue)
+ {
+ VerifyDimensions(data, x, y);
+ lock (locker)
+ {
+ this.xArr = x;
+ this.yArr = y;
+ this.data = data;
+ this.missingValue = missingValue;
+ dataVersion++;
+ }
+
+ InvalidateBounds();
+
+ return QueueRenderTask();
+ }
+
+ /// Plots rectangular heatmap where some data may be missing.
+ /// If size dimensions are equal to lenghtes of corresponding grid parameters
+ /// and then Gradient render method is used. If
+ /// dimension are smaller by one then Bitmap render method is used for heatmap. In all other cases exception is thrown.
+ /// Double, float, integer and boolean types are supported as data and grid array elements
+ /// Two dimensional array of data.
+ /// Grid along x axis.
+ /// Grid along y axis.
+ /// Missing value. Data items equal to aren't shown.
+ /// ID of background operation. You can subscribe to
+ /// notification to be notified when this operation is completed or request is dropped.
+ public long Plot(T[,] data, A[] x, A[] y, T missingValue)
+ {
+ return Plot(ArrayExtensions.ToDoubleArray2D(data),
+ ArrayExtensions.ToDoubleArray(x),
+ ArrayExtensions.ToDoubleArray(y),
+ Convert.ToDouble(missingValue, CultureInfo.InvariantCulture));
+ }
+
+ /// Plots rectangular heatmap where some data may be missing.
+ /// If size dimensions are equal to lenghtes of corresponding grid parameters
+ /// and then Gradient render method is used. If
+ /// dimension are smaller by one then Bitmap render method is used for heatmap. In all other cases exception is thrown.
+ /// Double, float, integer and boolean types are supported as data and grid array elements
+ /// Two dimensional array of data.
+ /// Grid along x axis.
+ /// Grid along y axis.
+ /// ID of background operation. You can subscribe to
+ /// notification to be notified when this operation is completed or request is dropped.
+ public long Plot(T[,] data, A[] x, A[] y)
+ {
+ return Plot(ArrayExtensions.ToDoubleArray2D(data),
+ ArrayExtensions.ToDoubleArray(x),
+ ArrayExtensions.ToDoubleArray(y),
+ Double.NaN);
+ }
+
+
+ /// Returns content bounds of this elements in cartesian coordinates.
+ /// Rectangle with content bounds.
+ protected override DataRect ComputeBounds()
+ {
+ if (xArr != null && yArr != null)
+ return new DataRect(xArr[0], yArr[0], xArr[xArr.Length - 1], yArr[yArr.Length - 1]);
+ else
+ return DataRect.Empty;
+ }
+
+ ///
+ /// Cached value of property. Accessed both from UI and rendering thread.
+ ///
+ private IPalette palette = InteractiveDataDisplay.WPF.Palette.Heat;
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty PaletteProperty = DependencyProperty.Register(
+ "Palette",
+ typeof(Palette),
+ typeof(HeatmapGraph),
+ new PropertyMetadata(InteractiveDataDisplay.WPF.Palette.Heat, OnPalettePropertyChanged));
+
+ /// Gets or sets the palette for heatmap rendering.
+ [TypeConverter(typeof(StringToPaletteTypeConverter))]
+ [Category("InteractiveDataDisplay")]
+ [Description("Defines mapping from values to color")]
+ public IPalette Palette
+ {
+ get { return (IPalette)GetValue(PaletteProperty); }
+ set { SetValue(PaletteProperty, value); }
+ }
+
+ private bool paletteRangeUpdateRequired = true;
+
+ private static void OnPalettePropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
+ {
+ HeatmapGraph heatmap = (HeatmapGraph)sender;
+ lock (heatmap.locker)
+ {
+ heatmap.paletteRangeUpdateRequired = true;
+ heatmap.palette = (Palette)e.NewValue;
+ }
+ heatmap.QueueRenderTask();
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty PaletteRangeProperty = DependencyProperty.Register(
+ "PaletteRange",
+ typeof(Range),
+ typeof(HeatmapGraph),
+ new PropertyMetadata(new Range(0, 1), OnPaletteRangePropertyChanged));
+
+ ///
+ /// Cached range of data values. It is accessed from UI and rendering thread.
+ ///
+ private Range dataRange = new Range(0, 1);
+
+ /// Version of data for current data range. If dataVersion != dataRangeVersion then
+ /// data range version should be recalculated.
+ private long dataRangeVersion = -1;
+
+ private int insidePaletteRangeSetter = 0;
+
+ /// Gets range of data values used in palette building.
+ /// This property cannot be set from outside code. Attempt to set it from
+ /// bindings result in exception.
+ [Browsable(false)]
+ public Range PaletteRange
+ {
+ get { return (Range)GetValue(PaletteRangeProperty); }
+ protected set
+ {
+ try
+ {
+ insidePaletteRangeSetter++;
+ SetValue(PaletteRangeProperty, value);
+ }
+ finally
+ {
+ insidePaletteRangeSetter--;
+ }
+ }
+ }
+
+ private static void OnPaletteRangePropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
+ {
+ var heatmap = (HeatmapGraph)sender;
+ if (heatmap.insidePaletteRangeSetter <= 0)
+ throw new InvalidOperationException("Palette Range property cannot be changed by binding. Use Palette property instead");
+ }
+
+ private void UpdatePaletteRange(long localDataVersion)
+ {
+ if (dataVersion != localDataVersion)
+ return;
+ paletteRangeUpdateRequired = false;
+ if (palette.IsNormalized)
+ PaletteRange = dataRange;
+ else
+ PaletteRange = palette.Range;
+ }
+
+ /// Gets range of data values for current data.
+ public Range DataRange
+ {
+ get
+ {
+ if (data != null && dataVersion != dataRangeVersion)
+ {
+ var r = Double.IsNaN(missingValue) ?
+ HeatmapBuilder.GetMaxMin(data) :
+ HeatmapBuilder.GetMaxMin(data, missingValue);
+ lock (locker)
+ {
+ dataRangeVersion = dataVersion;
+ dataRange = r;
+ }
+ UpdatePaletteRange(dataVersion);
+ }
+ return dataRange;
+ }
+ }
+
+ ///
+ /// Renders frame and returns it as a render result.
+ ///
+ /// Render task state for rendering frame.
+ /// Render result of rendered frame.
+ protected override RenderResult RenderFrame(RenderTaskState state)
+ {
+ if (state == null)
+ throw new ArgumentNullException("state");
+
+ if (!state.Bounds.IsEmpty && !state.IsCanceled && data != null)
+ {
+ //DataRect dataRect = new DataRect(state.Transform.Visible);
+ //Rect output = state.Transform.Screen;
+ DataRect dataRect = state.ActualPlotRect;
+ DataRect output = new DataRect(0, 0, state.ScreenSize.Width, state.ScreenSize.Height);
+ DataRect bounds = state.Bounds;
+
+ if (dataRect.XMin >= bounds.XMax || dataRect.XMax <= bounds.XMin ||
+ dataRect.YMin >= bounds.YMax || dataRect.YMax <= bounds.YMin)
+ return null;
+
+ double left = 0;
+ double xmin = dataRect.XMin;
+ double scale = output.Width / dataRect.Width;
+ if (xmin < bounds.XMin)
+ {
+ left = (bounds.XMin - dataRect.XMin) * scale;
+ xmin = bounds.XMin;
+ }
+
+ double width = output.Width - left;
+ double xmax = dataRect.XMax;
+ if (xmax > bounds.XMax)
+ {
+ width -= (dataRect.XMax - bounds.XMax) * scale;
+ xmax = bounds.XMax;
+ }
+
+ scale = output.Height / dataRect.Height;
+ double top = 0;
+ double ymax = dataRect.YMax;
+ if (ymax > bounds.YMax)
+ {
+ top = (dataRect.YMax - bounds.YMax) * scale;
+ ymax = bounds.YMax;
+ }
+
+ double height = output.Height - top;
+ double ymin = dataRect.YMin;
+ if (ymin < bounds.YMin)
+ {
+ height -= (bounds.YMin - dataRect.YMin) * scale;
+ ymin = bounds.YMin;
+ }
+
+ if (xmin < bounds.XMin)
+ xmin = bounds.XMin;
+ if (xmax > bounds.XMax)
+ xmax = bounds.XMax;
+ if (ymin < bounds.YMin)
+ ymin = bounds.YMin;
+ if (ymax > bounds.YMax)
+ ymax = bounds.YMax;
+
+ DataRect visibleData = new DataRect(xmin, ymin, xmax, ymax);
+
+ // Capture data to local variable
+ double[,] localData;
+ double[] localX, localY;
+ long localDataVersion;
+ IPalette localPalette;
+ double localMV;
+ Range localDataRange;
+ bool getMaxMin = false;
+ lock (locker)
+ {
+ localData = data;
+ localX = xArr;
+ localY = yArr;
+ localDataVersion = dataVersion;
+ localPalette = palette;
+ localMV = missingValue;
+ localDataRange = dataRange;
+ if (palette.IsNormalized && dataVersion != dataRangeVersion)
+ getMaxMin = true;
+ }
+ if (getMaxMin)
+ {
+ localDataRange = Double.IsNaN(missingValue) ?
+ HeatmapBuilder.GetMaxMin(data) :
+ HeatmapBuilder.GetMaxMin(data, missingValue);
+ lock (locker)
+ {
+ if (dataVersion == localDataVersion)
+ {
+ dataRangeVersion = dataVersion;
+ dataRange = localDataRange;
+ }
+ else
+ return null; // New data was passed to Plot method so this render task is obsolete
+ }
+ }
+ if (paletteRangeUpdateRequired)
+ Dispatcher.BeginInvoke(new Action(UpdatePaletteRange), localDataVersion);
+ return new RenderResult(HeatmapBuilder.BuildHeatMap(new Rect(0, 0, width, height),
+ visibleData, localX, localY, localData, localMV, localPalette, localDataRange), visibleData, new Point(left, top), width, height);
+ }
+ else
+ return null;
+ }
+
+ /// Gets or sets function to get tooltip object (string or UIElement)
+ /// for given screen point.
+ /// method is called by default.
+ public Func TooltipContentFunc
+ {
+ get;
+ set;
+ }
+
+ ///
+ /// Returns the string that is shown in tooltip for the screen point. If there is no data for this point (or nearest points) on a screen then returns null.
+ ///
+ /// A point to show tooltip for.
+ /// An object.
+ public object GetTooltipForPoint(Point screenPoint)
+ {
+ double pointData;
+ Point nearest;
+ if (GetNearestPointAndValue(screenPoint, out nearest, out pointData))
+ return String.Format(CultureInfo.InvariantCulture, "Data: {0}; X: {1}; Y: {2}", pointData, nearest.X, nearest.Y);
+ else
+ return null;
+ }
+
+ ///
+ /// Finds the point nearest to a specified point on a screen.
+ ///
+ /// The point to search nearest for.
+ /// The out parameter to handle the founded point.
+ /// The out parameter to handle data of founded point.
+ /// Boolen value indicating whether the nearest point was found or not.
+ public bool GetNearestPointAndValue(Point screenPoint, out Point nearest, out double vd)
+ {
+ nearest = new Point(Double.NaN, Double.NaN);
+ vd = Double.NaN;
+ if (data == null || xArr == null || yArr == null)
+ return false;
+ Point dataPoint = new Point(XDataTransform.PlotToData(XFromLeft(screenPoint.X)), YDataTransform.PlotToData(YFromTop(screenPoint.Y)));//PlotContext.ScreenToData(screenPoint);
+ int i = ArrayExtensions.GetNearestIndex(xArr, dataPoint.X);
+ if (i < 0)
+ return false;
+ int j = ArrayExtensions.GetNearestIndex(yArr, dataPoint.Y);
+ if (j < 0)
+ return false;
+ if (IsBitmap)
+ {
+ if (i > 0 && xArr[i - 1] > dataPoint.X)
+ i--;
+ if (j > 0 && yArr[j - 1] > dataPoint.Y)
+ j--;
+ }
+ if (i < data.GetLength(0) && j < data.GetLength(1))
+ {
+ vd = data[i, j];
+ nearest = new Point(xArr[i], yArr[j]);
+ return true;
+ }
+ else
+ return false;
+ }
+
+ ///
+ /// Gets the boolen value indicating whether heatmap is rendered using gradient filling.
+ ///
+ public bool IsGradient
+ {
+ get
+ {
+ return (data == null || xArr == null) ? false : (data.GetLength(0) == xArr.Length);
+ }
+ }
+
+ ///
+ /// Gets the boolen value indicating whether heatmap is rendered as a bitmap.
+ ///
+ public bool IsBitmap
+ {
+ get
+ {
+ return (data == null || xArr == null) ? false : (data.GetLength(0) == xArr.Length - 1);
+ }
+ }
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Plots/Heatmap/HeatmapTooltipLayer.cs b/InteractiveDataDisplay_OneDll/src/Plots/Heatmap/HeatmapTooltipLayer.cs
new file mode 100644
index 000000000..a6a207d3b
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Plots/Heatmap/HeatmapTooltipLayer.cs
@@ -0,0 +1,219 @@
+// Copyright © Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+//// Copyright © Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+//
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Collections.Generic;
+using System.Windows.Threading;
+using System.ComponentModel;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Control for displaying tooltips for .
+ /// Shows a value of heatmap in specified point.
+ ///
+ [Description("Displays value of heatmap under cursor")]
+ public class HeatmapTooltipLayer : Panel
+ {
+ private Point location;
+ private Object content;
+ private DispatcherTimer dispatcherTimer;
+ private ToolTip toolTip = new ToolTip();
+ private TimeSpan dueTime = new TimeSpan(0, 0, 1);
+ private TimeSpan durationInterval = new TimeSpan(0, 0, 7);
+ private IDisposable subscription;
+ private Dictionary heatmapSubscriptions = new Dictionary();
+ private PlotBase parent = null;
+
+ ///
+ /// Initializes new instance of class.
+ ///
+ public HeatmapTooltipLayer()
+ {
+ ContentFunc = DefaultContentFunc;
+ this.toolTip.Placement = System.Windows.Controls.Primitives.PlacementMode.Mouse;
+ this.toolTip.VerticalOffset = 10;
+ this.toolTip.HorizontalOffset = 5;
+ this.MouseMove += new MouseEventHandler(OnMouseMove);
+ this.MouseLeave += new MouseEventHandler(TooltipLayer_MouseLeave);
+ this.toolTip.IsOpen = false;
+
+ this.dispatcherTimer = new DispatcherTimer();
+ this.dispatcherTimer.Interval = dueTime;
+ this.dispatcherTimer.Tick += OnTick;
+
+ this.Loaded += new RoutedEventHandler(HeatmapTooltipLayer_Loaded);
+ this.Unloaded += new RoutedEventHandler(HeatmapTooltipLayer_Unloaded);
+ }
+
+ void HeatmapTooltipLayer_Loaded(object sender, RoutedEventArgs e)
+ {
+ var visualParent = VisualTreeHelper.GetParent(this);
+ parent = visualParent as PlotBase;
+ while(visualParent != null && parent == null)
+ {
+ visualParent = VisualTreeHelper.GetParent(visualParent);
+ parent = visualParent as PlotBase;
+ }
+ if (parent != null)
+ {
+ parent.MouseMove += new MouseEventHandler(OnMouseMove);
+ parent.MouseLeave += new MouseEventHandler(TooltipLayer_MouseLeave);
+ }
+ }
+
+ void HeatmapTooltipLayer_Unloaded(object sender, RoutedEventArgs e)
+ {
+ subscription.Dispose();
+ foreach (IDisposable s in heatmapSubscriptions.Values)
+ {
+ s.Dispose();
+ }
+ heatmapSubscriptions.Clear();
+ }
+
+ private void RefreshTooltip()
+ {
+ object result = ContentFunc(this.location);
+ if (result == null)
+ {
+ toolTip.Content = null;
+ toolTip.IsOpen = false;
+ return;
+ }
+ toolTip.Content = result;
+ }
+
+ private void TooltipLayer_MouseLeave(object sender, MouseEventArgs e)
+ {
+ this.Hide();
+ }
+
+ ///
+ /// Gets or sets the tooltip content.
+ ///
+ [Browsable(false)]
+ public Object Content
+ {
+ get { return content; }
+ set { content = value; }
+ }
+
+ ///
+ /// Tooltip content function.
+ ///
+ [Browsable(false)]
+ public Func ContentFunc
+ {
+ get;
+ set;
+ }
+
+ void OnMouseMove(object sender, MouseEventArgs e)
+ {
+ this.location = e.GetPosition(this);
+ if (toolTip.IsOpen)
+ {
+ this.Hide();
+ }
+
+ if (dispatcherTimer.IsEnabled)
+ {
+ dispatcherTimer.Stop();
+ }
+
+ dispatcherTimer.Interval = dueTime;
+ dispatcherTimer.Start();
+ }
+
+ void OnTick(object sender, EventArgs e)
+ {
+ dispatcherTimer.Stop();
+
+ bool show = !toolTip.IsOpen;
+ object result = ContentFunc(location);
+ if (result == null)
+ {
+ return;
+ }
+ toolTip.Content = result;
+ toolTip.IsOpen = show;
+ if (show)
+ HideDelayed(durationInterval);
+ }
+
+ ///
+ /// Default function for building tooltip content.
+ ///
+ /// Point in which the value is requied.
+ /// A content for tooltip for specified point.
+ public virtual object DefaultContentFunc(Point pt)
+ {
+ if (parent == null)
+ return null;
+ StackPanel contentPanel = new StackPanel
+ {
+ Orientation = Orientation.Vertical,
+ };
+ foreach (UIElement elem in parent.RelatedPlots)
+ {
+ if (elem is HeatmapGraph)
+ {
+ ITooltipProvider el = elem as ITooltipProvider;
+ if (el != null)
+ {
+ var control = new ContentControl();
+ control.IsTabStop = false;
+ if (el.TooltipContentFunc != null)
+ {
+ var internalContent = el.TooltipContentFunc(pt);
+ if (internalContent != null)
+ {
+ control.Content = internalContent;
+ contentPanel.Children.Add(control);
+ }
+ }
+ }
+ }
+ }
+ return contentPanel.Children.Count > 0 ? contentPanel : null;
+ }
+
+ private void Hide()
+ {
+ if (dispatcherTimer.IsEnabled)
+ dispatcherTimer.Stop();
+ toolTip.IsOpen = false;
+ }
+
+
+ private void HideDelayed(TimeSpan delay)
+ {
+ if (dispatcherTimer.IsEnabled)
+ dispatcherTimer.Stop();
+
+ dispatcherTimer.Interval = delay;
+ dispatcherTimer.Start();
+ }
+ }
+
+ ///
+ /// Interface for elements which can provide content for tooltips.
+ ///
+ public interface ITooltipProvider
+ {
+ ///
+ /// Tooltip content generation function.
+ ///
+ Func TooltipContentFunc { get; }
+ }
+}
+
+
diff --git a/InteractiveDataDisplay_OneDll/src/Plots/Line/LineGraph.cs b/InteractiveDataDisplay_OneDll/src/Plots/Line/LineGraph.cs
new file mode 100644
index 000000000..84b8f08f4
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Plots/Line/LineGraph.cs
@@ -0,0 +1,275 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Shapes;
+using System.Windows.Data;
+using System.Collections;
+using System.Globalization;
+using System.ComponentModel;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// A plot to draw simple line.
+ ///
+ [Description("Plots line graph")]
+ public class LineGraph : Plot
+ {
+ private Polyline polyline;
+
+ ///
+ /// Gets or sets line graph points.
+ ///
+ [Category("InteractiveDataDisplay")]
+ [Description("Line graph points")]
+ public PointCollection Points
+ {
+ get { return (PointCollection)GetValue(PointsProperty); }
+ set { SetValue(PointsProperty, value); }
+ }
+
+ private static void PointsPropertyChangedHandler(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ LineGraph linePlot = (LineGraph)d;
+ if (linePlot != null)
+ {
+ InteractiveDataDisplay.WPF.Plot.SetPoints(linePlot.polyline, (PointCollection)e.NewValue);
+ }
+ }
+
+ ///
+ /// Initializes a new instance of class.
+ ///
+ public LineGraph()
+ {
+ polyline = new Polyline
+ {
+ Stroke = new SolidColorBrush(Colors.Black),
+ StrokeLineJoin = PenLineJoin.Round
+ };
+
+ BindingOperations.SetBinding(polyline, Polyline.StrokeThicknessProperty, new Binding("StrokeThickness") { Source = this });
+ BindingOperations.SetBinding(this, PlotBase.PaddingProperty, new Binding("StrokeThickness") { Source = this, Converter = new LineGraphThicknessConverter() });
+
+ Children.Add(polyline);
+ }
+ static LineGraph()
+ {
+ PointsProperty.OverrideMetadata(typeof(LineGraph), new PropertyMetadata(new PointCollection(), PointsPropertyChangedHandler) );
+ }
+
+ ///
+ /// Updates data in and causes a redrawing of line graph.
+ ///
+ /// A set of x coordinates of new points.
+ /// A set of y coordinates of new points.
+ public void Plot(IEnumerable x, IEnumerable y)
+ {
+ if (x == null)
+ throw new ArgumentNullException("x");
+ if (y == null)
+ throw new ArgumentNullException("y");
+
+ var points = new PointCollection();
+ var enx = x.GetEnumerator();
+ var eny = y.GetEnumerator();
+ while (true)
+ {
+ var nx = enx.MoveNext();
+ var ny = eny.MoveNext();
+ if (nx && ny)
+ points.Add(new Point(Convert.ToDouble(enx.Current, CultureInfo.InvariantCulture),
+ Convert.ToDouble(eny.Current, CultureInfo.InvariantCulture)));
+ else if (!nx && !ny)
+ break;
+ else
+ throw new ArgumentException("x and y have different lengthes");
+ }
+
+ Points = points;
+ }
+
+ ///
+ /// Updates data in and causes a redrawing of line graph.
+ /// In this version a set of x coordinates is a sequence of integers starting with zero.
+ ///
+ /// A set of y coordinates of new points.
+ public void PlotY(IEnumerable y)
+ {
+ if (y == null)
+ throw new ArgumentNullException("y");
+ int x = 0;
+ var en = y.GetEnumerator();
+ var points = new PointCollection();
+ while (en.MoveNext())
+ points.Add(new Point(x++, Convert.ToDouble(en.Current, CultureInfo.InvariantCulture)));
+
+ Points = points;
+ }
+
+ #region Description
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty DescriptionProperty =
+ DependencyProperty.Register("Description",
+ typeof(string),
+ typeof(LineGraph),
+ new PropertyMetadata(null,
+ (s, a) =>
+ {
+ var lg = (LineGraph)s;
+ ToolTipService.SetToolTip(lg, a.NewValue);
+ }));
+
+ ///
+ /// Gets or sets description text for line graph. Description text appears in default
+ /// legend and tooltip.
+ ///
+ [Category("InteractiveDataDisplay")]
+ public string Description
+ {
+ get
+ {
+ return (string)GetValue(DescriptionProperty);
+ }
+ set
+ {
+ SetValue(DescriptionProperty, value);
+ }
+ }
+
+ #endregion
+
+ #region Thickness
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty StrokeThicknessProperty =
+ DependencyProperty.Register("StrokeThickness",
+ typeof(double),
+ typeof(LineGraph),
+ new PropertyMetadata(1.0));
+
+ ///
+ /// Gets or sets the line thickness.
+ ///
+ ///
+ /// The default stroke thickness is 1.0
+ ///
+ [Category("Appearance")]
+ public double StrokeThickness
+ {
+ get
+ {
+ return (double)GetValue(StrokeThicknessProperty);
+ }
+ set
+ {
+ SetValue(StrokeThicknessProperty, value);
+ }
+ }
+ #endregion
+
+ #region Stroke
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty StrokeProperty =
+ DependencyProperty.Register("Stroke",
+ typeof(Brush),
+ typeof(LineGraph),
+ new PropertyMetadata(new SolidColorBrush(Colors.Black), OnStrokeChanged));
+
+ private static void OnStrokeChanged(object target, DependencyPropertyChangedEventArgs e)
+ {
+ LineGraph lineGraph = (LineGraph)target;
+ lineGraph.polyline.Stroke = e.NewValue as Brush;
+ }
+
+ ///
+ /// Gets or sets the brush to draw the line.
+ ///
+ ///
+ /// The default color of stroke is black
+ ///
+ [Category("Appearance")]
+ public Brush Stroke
+ {
+ get
+ {
+ return (Brush)GetValue(StrokeProperty);
+ }
+ set
+ {
+ SetValue(StrokeProperty, value);
+ }
+ }
+ #endregion
+
+ #region StrokeDashArray
+
+ private static DoubleCollection EmptyDoubleCollection
+ {
+ get
+ {
+ var result = new DoubleCollection(0);
+ result.Freeze();
+ return result;
+ }
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty StrokeDashArrayProperty =
+ DependencyProperty.Register("StrokeDashArray",
+ typeof(DoubleCollection),
+ typeof(LineGraph),
+ new PropertyMetadata(EmptyDoubleCollection, OnStrokeDashArrayChanged));
+
+ private static void OnStrokeDashArrayChanged(object target, DependencyPropertyChangedEventArgs e)
+ {
+ LineGraph lineGraph = (LineGraph)target;
+ lineGraph.polyline.StrokeDashArray = e.NewValue as DoubleCollection;
+ }
+
+ ///
+ /// Gets or sets a collection of values that indicate the pattern of dashes and gaps that is used to draw the line.
+ ///
+ [Category("Appearance")]
+ public DoubleCollection StrokeDashArray
+ {
+ get
+ {
+ return (DoubleCollection)GetValue(StrokeDashArrayProperty);
+ }
+ set
+ {
+ SetValue(StrokeDashArrayProperty, value);
+ }
+ }
+ #endregion
+ }
+
+ internal class LineGraphThicknessConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+ {
+ double thickness = (double)value;
+ return new Thickness(thickness / 2.0);
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Plots/Markers/CartesianMarkerGraph.cs b/InteractiveDataDisplay_OneDll/src/Plots/Markers/CartesianMarkerGraph.cs
new file mode 100644
index 000000000..d48ac4fb5
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Plots/Markers/CartesianMarkerGraph.cs
@@ -0,0 +1,1001 @@
+// Copyright © Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Windows;
+using System.Globalization;
+using System.ComponentModel;
+using System.Collections;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// A base class for all instances in cartesian coordinates.
+ /// Has predefined X and Y .
+ ///
+ [Description("Marker graph in cartesian coordinates")]
+ public class CartesianMarkerGraph : MarkerGraph
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CartesianMarkerGraph()
+ {
+ Sources.Add(new DataSeries { Key = "X", Description = "X" });
+ Sources.Add(new DataSeries { Key = "Y", Description = "Y" });
+ }
+ ///
+ /// Updates data in data series and starts redrawing marker graph. This method returns before
+ /// marker graph is redrawn. This method is thread safe.
+ /// This version does not need specification of X . Default value is a sequence of integers starting with zero.
+ ///
+ /// Data for Y defining y coordinates of markers.
+ /// Can be a single value (to draw one marker or markers with the same y coordinates)
+ /// or an array or IEnumerable (to draw markers with different y coordinates) of any numeric type.
+ /// Can be null then no markers will be drawn.
+ ///
+ /// ID of rendering task. You can subscribe to notification about rendering completion events
+ /// as observer of RenderCompletion.
+ public virtual long PlotY(object y)
+ {
+ return Plot(null, y);
+ }
+ ///
+ /// Updates data in data series and starts redrawing marker graph. This method returns before
+ /// marker graph is redrawn. This method is thread safe.
+ ///
+ /// Data for X defining x coordinates of markers.
+ /// Can be a single value (to draw one marker or markers with the same x coordinates)
+ /// or an array or IEnumerable (to draw markers with different x coordinates) of any numeric type.
+ /// Can be null then x coordinates will be a sequence of integers starting with zero.
+ ///
+ /// Data for Y defining y coordinates of markers.
+ /// Can be a single value (to draw one marker or markers with the same y coordinates)
+ /// or an array or IEnumerable (to draw markers with different y coordinates) of any numeric type.
+ /// Can be null then no markers will be drawn.
+ ///
+ ///
+ /// Note that all vector data for should be of the same length. Otherwise no markers will be drawn.
+ ///
+ /// ID of rendering task. You can subscribe to notification about rendering completion events
+ /// as observer of RenderCompletion.
+ public virtual long PlotXY(object x, object y)
+ {
+ return Plot(x, y);
+ }
+ #region X
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty XProperty = DependencyProperty.Register(
+ "X", typeof(object), typeof(CartesianMarkerGraph), new PropertyMetadata(null, OnXChanged));
+ ///
+ /// Gets or sets the data for X defining x coordinates of markers.
+ /// Can be a single value (to draw one marker or markers with the same x coordinates)
+ /// or an array or IEnumerable (to draw markers with different x coordinates) of any numeric type.
+ /// Can be null then x coordinates will be a sequence of integers starting with zero.
+ /// Default value is null.
+ ///
+ [Category("InteractiveDataDisplay")]
+ [Description("X coordinates")]
+ public object X
+ {
+ get { return (object)GetValue(XProperty); }
+ set { SetValue(XProperty, value); }
+ }
+ private static void OnXChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ CartesianMarkerGraph mg = (CartesianMarkerGraph)d;
+ mg.Sources["X"].Data = e.NewValue;
+ }
+ #endregion
+ #region Y
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty YProperty = DependencyProperty.Register(
+ "Y", typeof(object), typeof(CartesianMarkerGraph), new PropertyMetadata(null, OnYChanged));
+
+ ///
+ /// Gets or sets the data for Y defining y coordinates of markers.
+ /// Can be a single value (to draw one marker or markers with the same y coordinates)
+ /// or an array or IEnumerable (to draw markers with different y coordinates) of any numeric type.
+ /// Can be null then no markers will be drawn.
+ /// Default value is null.
+ ///
+ [Category("InteractiveDataDisplay")]
+ [Description("Y coordinates")]
+ public object Y
+ {
+ get { return (object)GetValue(YProperty); }
+ set { SetValue(YProperty, value); }
+ }
+
+ ///
+ /// Called when property changes.
+ ///
+ /// Old value of propery .
+ /// New value of propery .
+ protected virtual void OnYChanged(object oldValue, object newValue)
+ {
+ Sources["Y"].Data = newValue;
+ }
+
+ private static void OnYChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ CartesianMarkerGraph mg = (CartesianMarkerGraph)d;
+ mg.OnYChanged(e.OldValue, e.NewValue);
+ }
+ #endregion
+ #region Description
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(
+ "Description", typeof(string), typeof(CartesianMarkerGraph), new PropertyMetadata(null, OnDescriptionChanged));
+
+ ///
+ /// Gets or sets the description of Y . Is frequently used in legend and tooltip.
+ /// Default value is null.
+ ///
+ [Category("InteractiveDataDisplay")]
+ [Description("Text for tooltip and legend")]
+ public string Description
+ {
+ get { return (string)GetValue(DescriptionProperty); }
+ set { SetValue(DescriptionProperty, value); }
+ }
+
+ private static void OnDescriptionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ CartesianMarkerGraph mg = (CartesianMarkerGraph)d;
+ mg.Sources["Y"].Description = (string)e.NewValue;
+ }
+ #endregion
+ }
+ ///
+ /// Is a with defined and
+ /// to draw markers in cartesian coordinates in color and of different size.
+ ///
+ /// Note that legend and tooltip templates are choosing automaticaly depending on a using version of Plot method.
+ ///
+ ///
+ [Description("Marker graph in cartesian space with color and size as additional series")]
+ public class CartesianSizeColorMarkerGraph : CartesianMarkerGraph
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CartesianSizeColorMarkerGraph()
+ {
+ Sources.Add(new ColorSeries());
+ Sources.Add(new SizeSeries());
+ }
+
+ ///
+ /// Updates data in data series and starts redrawing marker graph. This method returns before
+ /// marker graph is redrawn. This method is not thread safe.
+ /// This version does not need specification of X . Default value is a sequence of integers starting with zero.
+ /// Color and size of markers are taken from and dependency properties.
+ ///
+ /// Data for Y defining y coordinates of markers.
+ /// Can be a single value (to draw one marker or markers with the same y coordinates)
+ /// or an array or IEnumerable (to draw markers with different y coordinates) of any numeric type.
+ /// Can be null then no markers will be drawn.
+ ///
+ /// ID of rendering task. You can subscribe to notification about rendering completion events
+ /// as observer of RenderCompletion.
+ public override long PlotY(object y)
+ {
+ LegendTemplate = GetLegendTemplate(Color, Size);
+ TooltipTemplate = GetTooltipTemplate(Color, Size);
+ return Plot(null, y, Color, Size);
+ }
+ ///
+ /// Updates data in data series and starts redrawing marker graph. This method returns before
+ /// marker graph is redrawn. This method is not thread safe.
+ /// Color and size of markers are getting from and dependency properties.
+ ///
+ /// Data for X defining x coordinates of markers.
+ /// Can be a single value (to draw one marker or markers with the same x coordinates)
+ /// or an array or IEnumerable (to draw markers with different x coordinates) of any numeric type.
+ /// Can be null then x coordinates will be a sequence of integers starting with zero.
+ ///
+ /// Data for Y defining y coordinates of markers.
+ /// Can be a single value (to draw one marker or markers with the same y coordinates)
+ /// or an array or IEnumerable (to draw markers with different y coordinates) of any numeric type.
+ /// Can be null then no markers will be drawn.
+ ///
+ ///
+ /// Note that all vector data for should be of the same length. Otherwise no markers will be drawn.
+ ///
+ /// ID of rendering task. You can subscribe to notification about rendering completion events
+ /// as observer of RenderCompletion.
+ public override long PlotXY(object x, object y)
+ {
+ LegendTemplate = GetLegendTemplate(Color, Size);
+ TooltipTemplate = GetTooltipTemplate(Color, Size);
+
+ return Plot(x, y, Color, Size);
+ }
+ ///
+ /// Updates data in data series and starts redrawing marker graph. This method returns before
+ /// marker graph is redrawn. This method is not thread safe.
+ /// Size of markers is getting from dependency property.
+ ///
+ /// Data for X defining x coordinates of markers.
+ /// Can be a single value (to draw one marker or markers with the same x coordinates)
+ /// or an array or IEnumerable (to draw markers with different x coordinates) of any numeric type.
+ /// Can be null then x coordinates will be a sequence of integers starting with zero.
+ ///
+ /// Data for Y defining y coordinates of markers.
+ /// Can be a single value (to draw one marker or markers with the same y coordinates)
+ /// or an array or IEnumerable (to draw markers with different y coordinates) of any numeric type.
+ /// Can be null then no markers will be drawn.
+ ///
+ /// Data for defining color of markers.
+ /// Can be a single value (to draw markers of one color) or an array or IEnumerable
+ /// (to draw markers of different colors) of any numeric type, System.Windows.Media.Color,
+ /// or string defining system name of a color.
+ /// Can be null then no markers will be drawn.
+ ///
+ ///
+ /// Note that all vector data for should be of the same length. Otherwise no markers will be drawn.
+ ///
+ /// ID of rendering task. You can subscribe to notification about rendering completion events
+ /// as observer of RenderCompletion.
+ public long PlotColor(object x, object y, object c)
+ {
+ LegendTemplate = GetLegendTemplate(c, Size);
+ TooltipTemplate = GetTooltipTemplate(c, Size);
+
+ return Plot(x, y, c, Size);
+ }
+ ///
+ /// Updates data in data series and starts redrawing marker graph. This method returns before
+ /// marker graph is redrawn. This method is thread safe.
+ /// Color of markers is getting from dependency property.
+ ///
+ /// Data for X defining x coordinates of markers.
+ /// Can be a single value (to draw one marker or markers with the same x coordinates)
+ /// or an array or IEnumerable (to draw markers with different x coordinates) of any numeric type.
+ /// Can be null then x coordinates will be a sequence of integers starting with zero.
+ ///
+ /// Data for Y defining y coordinates of markers.
+ /// Can be a single value (to draw one marker or markers with the same y coordinates)
+ /// or an array or IEnumerable (to draw markers with different y coordinates) of any numeric type.
+ /// Can be null then no markers will be drawn.
+ ///
+ /// Data for defining size of markers in screen coordinates.
+ /// Can be a single value (to draw markers of one size) or an array or IEnumerable (to draw markers of different sizes) of
+ /// any numeric type. Can be null then no markers will be drawn.
+ ///
+ ///
+ /// Note that all vector data for should be of the same length. Otherwise no markers will be drawn.
+ ///
+ /// ID of rendering task. You can subscribe to notification about rendering completion events
+ /// as observer of RenderCompletion.
+ public long PlotSize(object x, object y, object d)
+ {
+ LegendTemplate = GetLegendTemplate(Color, d);
+ TooltipTemplate = GetTooltipTemplate(Color, d);
+
+ return Plot(x, y, Color, d);
+ }
+ ///
+ /// Updates data in data series and starts redrawing marker graph. This method returns before
+ /// marker graph is redrawn. This method is not thread safe.
+ ///
+ /// Data for X defining x coordinates of markers.
+ /// Can be a single value (to draw one marker or markers with the same x coordinates)
+ /// or an array or IEnumerable (to draw markers with different x coordinates) of any numeric type.
+ /// Can be null then x coordinates will be a sequence of integers starting with zero.
+ ///
+ /// Data for Y defining y coordinates of markers.
+ /// Can be a single value (to draw one marker or markers with the same y coordinates)
+ /// or an array or IEnumerable (to draw markers with different y coordinates) of any numeric type.
+ /// Can be null then no markers will be drawn.
+ ///
+ /// Data for defining color of markers.
+ /// Can be a single value (to draw markers of one color) or an array or IEnumerable (to draw markers of different colors) of
+ /// any numeric type, System.Windows.Media.Color, or string defining system name of a color.
+ /// Can be null then no markers will be drawn.
+ ///
+ /// Data for defining size of markers in screen coordinates.
+ /// Can be a single value (to draw markers of one size) or an array or IEnumerable (to draw markers of different sizes) of
+ /// any numeric type. Can be null then no markers will be drawn.
+ ///
+ ///
+ /// Note that all vector data for should be of the same length. Otherwise no markers will be drawn.
+ ///
+ /// ID of rendering task. You can subscribe to notification about rendering completion events
+ /// as observer of RenderCompletion.
+ public long PlotColorSize(object x, object y, object c, object d)
+ {
+ LegendTemplate = GetLegendTemplate(c, d);
+ TooltipTemplate = GetTooltipTemplate(c, d);
+
+ return Plot(x, y, c, d);
+ }
+ private static bool IsScalarOrNull(object obj)
+ {
+ if (obj == null || obj is string)
+ {
+ return true;
+ }
+ IEnumerable ienum = obj as IEnumerable;
+ if (ienum != null)
+ {
+ int n = DataSeries.GetArrayFromEnumerable(ienum).Length;
+ if (n == 0 || n == 1)
+ return true;
+ return false;
+ }
+ Array array = obj as Array;
+ if (array != null)
+ {
+ int n = array.Length;
+ if (n == 0 || n == 1)
+ return true;
+ return array.Rank == 1;
+ }
+ return true;
+ }
+ private DataTemplate GetLegendTemplate(object c, object d)
+ {
+ if (!IsScalarOrNull(c) && !IsScalarOrNull(d))
+ return MarkerType.GetColorSizeLegendTemplate(this);
+ else if (!IsScalarOrNull(c))
+ return MarkerType.GetColorLegendTemplate(this);
+ else if (!IsScalarOrNull(d))
+ return MarkerType.GetSizeLegendTemplate(this);
+ else
+ return MarkerType.GetYLegendTemplate(this);
+ }
+ private DataTemplate GetTooltipTemplate(object c, object d)
+ {
+ if (!IsScalarOrNull(c) && !IsScalarOrNull(d))
+ return ColorSizeMarker.GetColorSizeTooltipTemplate(this);
+ else if (!IsScalarOrNull(c))
+ return ColorSizeMarker.GetColorTooltipTemplate(this);
+ else if (!IsScalarOrNull(d))
+ return ColorSizeMarker.GetSizeTooltipTemplate(this);
+ else
+ return ColorSizeMarker.GetYTooltipTemplate(this);
+ }
+ ///
+ /// Called when property Y changes.
+ ///
+ /// Old value of propery Y.
+ /// New value of propery Y.
+ protected override void OnYChanged(object oldValue, object newValue)
+ {
+ LegendTemplate = GetLegendTemplate(Color, Size);
+ TooltipTemplate = GetTooltipTemplate(Color, Size);
+ base.OnYChanged(oldValue, newValue);
+ }
+ #region Size
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty SizeProperty = DependencyProperty.Register(
+ "Size", typeof(object), typeof(CartesianSizeColorMarkerGraph), new PropertyMetadata(10.0, OnSizeChanged));
+
+ ///
+ /// Gets or sets the data for (key 'D').
+ /// Can be a single value (to draw markers of one size) or an array or IEnumerable (to draw markers of different sizes) of
+ /// any numeric type. Can be null then no markers will be drawn.
+ /// If properties and are not Double.NaN then
+ /// is used to transform values from original range of data to the range created from and .
+ /// Default value is 10.
+ ///
+ [Category("InteractiveDataDisplay")]
+ [Description("Series to define marker sizes")]
+ public object Size
+ {
+ get { return (object)GetValue(SizeProperty); }
+ set { SetValue(SizeProperty, value); }
+ }
+
+ private static void OnSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ CartesianSizeColorMarkerGraph mg = (CartesianSizeColorMarkerGraph)d;
+ mg.LegendTemplate = mg.GetLegendTemplate(mg.Color, e.NewValue);
+ mg.TooltipTemplate = mg.GetTooltipTemplate(mg.Color, e.NewValue);
+ mg.Sources["D"].Data = e.NewValue;
+ }
+ #endregion
+ #region Color
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty ColorProperty = DependencyProperty.Register(
+ "Color", typeof(object), typeof(CartesianSizeColorMarkerGraph), new PropertyMetadata("Black", OnColorChanged));
+
+ ///
+ /// Gets or sets the data for (key 'C').
+ /// Can be a single value (to draw markers of one color) or an array or IEnumerable (to draw markers of different colors) of
+ /// any numeric type, System.Windows.Media.Color, or string defining system name or hexadecimal representation (#AARRGGBB) of a color.
+ /// Can be null then no markers will be drawn.
+ /// Default value is black color.
+ ///
+ [Category("InteractiveDataDisplay")]
+ [Description("Series to define marker colors")]
+ public object Color
+ {
+ get { return (object)GetValue(ColorProperty); }
+ set { SetValue(ColorProperty, value); }
+ }
+
+ private static void OnColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ CartesianSizeColorMarkerGraph mg = (CartesianSizeColorMarkerGraph)d;
+ mg.LegendTemplate = mg.GetLegendTemplate(e.NewValue, mg.Size);
+ mg.TooltipTemplate = mg.GetTooltipTemplate(e.NewValue, mg.Size);
+ mg.Sources["C"].Data = e.NewValue;
+ }
+ #endregion
+ #region SizeDescription
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty SizeDescriptionProperty = DependencyProperty.Register(
+ "SizeDescription", typeof(string), typeof(CartesianSizeColorMarkerGraph),
+ new PropertyMetadata(null, OnSizeDescriptionChanged));
+
+ ///
+ /// Gets or sets the description of .
+ /// Default value is null.
+ ///
+ [Category("InteractiveDataDisplay")]
+ [Description("Size series description")]
+ public string SizeDescription
+ {
+ get { return (string)GetValue(SizeDescriptionProperty); }
+ set { SetValue(SizeDescriptionProperty, value); }
+ }
+
+ private static void OnSizeDescriptionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ CartesianSizeColorMarkerGraph mg = (CartesianSizeColorMarkerGraph)d;
+ mg.Sources["D"].Description = (string)e.NewValue;
+ }
+ #endregion
+ #region ColorDescription
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty ColorDescriptionProperty = DependencyProperty.Register(
+ "ColorDescription", typeof(string), typeof(CartesianSizeColorMarkerGraph),
+ new PropertyMetadata(null, OnColorDescriptionChanged));
+
+ ///
+ /// Gets or sets the description of .
+ /// Default value is null.
+ ///
+ [Category("InteractiveDataDisplay")]
+ [Description("Color series description")]
+ public string ColorDescription
+ {
+ get { return (string)GetValue(ColorDescriptionProperty); }
+ set { SetValue(ColorDescriptionProperty, value); }
+ }
+
+ private static void OnColorDescriptionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ CartesianSizeColorMarkerGraph mg = (CartesianSizeColorMarkerGraph)d;
+ mg.Sources["C"].Description = (string)e.NewValue;
+ }
+ #endregion
+ #region Min
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty MinProperty = DependencyProperty.Register(
+ "Min", typeof(double), typeof(CartesianSizeColorMarkerGraph), new PropertyMetadata(Double.NaN, OnMinChanged));
+
+ ///
+ /// Gets or sets screen size corresponding to minimal value in size series.
+ /// Default value is Double.NaN.
+ ///
+ [Category("InteractiveDataDisplay")]
+ [Description("Minimal screen size of marker")]
+ public double Min
+ {
+ get { return (double)GetValue(MinProperty); }
+ set { SetValue(MinProperty, value); }
+ }
+
+ private static void OnMinChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ CartesianSizeColorMarkerGraph mg = (CartesianSizeColorMarkerGraph)d;
+ (mg.Sources["D"] as SizeSeries).Min = Convert.ToDouble(e.NewValue, CultureInfo.InvariantCulture); ;
+ }
+ #endregion
+ #region Max
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty MaxProperty = DependencyProperty.Register(
+ "Max", typeof(double), typeof(CartesianSizeColorMarkerGraph), new PropertyMetadata(Double.NaN, OnMaxChanged));
+
+ ///
+ /// Gets or sets screen size corresponding to maximum value in size series.
+ /// Default value is Double.NaN.
+ ///
+ [Category("InteractiveDataDisplay")]
+ [Description("Maximum screen size of marker")]
+ public double Max
+ {
+ get { return (double)GetValue(MaxProperty); }
+ set { SetValue(MaxProperty, value); }
+ }
+
+ private static void OnMaxChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ CartesianSizeColorMarkerGraph mg = (CartesianSizeColorMarkerGraph)d;
+ (mg.Sources["D"] as SizeSeries).Max = Convert.ToDouble(e.NewValue, CultureInfo.InvariantCulture); ;
+ }
+ #endregion
+ #region Palette
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty PaletteProperty = DependencyProperty.Register(
+ "Palette", typeof(IPalette), typeof(CartesianSizeColorMarkerGraph), new PropertyMetadata(null, OnPaletteChanged));
+
+ ///
+ /// Gets or sets the color palette for markers. Defines mapping of values to colors.
+ /// Is used only if the data of is of numeric type.
+ /// Default value is null.
+ ///
+ [TypeConverter(typeof(StringToPaletteTypeConverter))]
+ [Category("InteractiveDataDisplay")]
+ [Description("Defines mapping of values to colors")]
+ public IPalette Palette
+ {
+ get { return (IPalette)GetValue(PaletteProperty); }
+ set { SetValue(PaletteProperty, value); }
+ }
+
+ private static void OnPaletteChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ CartesianSizeColorMarkerGraph mg = (CartesianSizeColorMarkerGraph)d;
+ (mg.Sources["C"] as ColorSeries).Palette = e.NewValue as IPalette;
+ }
+ #endregion
+ #region MarkerType
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty MarkerTypeProperty = DependencyProperty.Register(
+ "MarkerType", typeof(ColorSizeMarker), typeof(CartesianSizeColorMarkerGraph), new PropertyMetadata(null, OnMarkerTypeChanged));
+
+ ///
+ /// Gets or sets one of predefined types for markers.
+ /// Default value is null.
+ ///
+ [Category("InteractiveDataDisplay")]
+ [Description("One of predefined marker types")]
+ public ColorSizeMarker MarkerType
+ {
+ get { return (ColorSizeMarker)GetValue(MarkerTypeProperty); }
+ set { SetValue(MarkerTypeProperty, value); }
+ }
+
+ private static void OnMarkerTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ CartesianSizeColorMarkerGraph mg = (CartesianSizeColorMarkerGraph)d;
+ mg.MarkerTemplate = mg.MarkerType.GetMarkerTemplate(mg);
+ }
+ #endregion
+ }
+ ///
+ /// An abstract class providing methods to get templates for markers that use color and size.
+ ///
+ public abstract class ColorSizeMarker
+ {
+ ///
+ /// Gets a template for markers.
+ ///
+ /// Marker graph to get template for.
+ /// A template used for markers of particular marker graph.
+ public abstract DataTemplate GetMarkerTemplate(MarkerGraph mg);
+ ///
+ /// Gets template for marker legend with information about Y .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph.
+ public abstract DataTemplate GetYLegendTemplate(MarkerGraph mg);
+ ///
+ /// Gets template for marker legend with information about Y and .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph.
+ public abstract DataTemplate GetColorLegendTemplate(MarkerGraph mg);
+ ///
+ /// Gets template for marker legend with information about Y and .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph.
+ public abstract DataTemplate GetSizeLegendTemplate(MarkerGraph mg);
+ ///
+ /// Gets template for marker legend with information about Y ,
+ /// and .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph.
+ public abstract DataTemplate GetColorSizeLegendTemplate(MarkerGraph mg);
+ ///
+ /// Gets template for marker tooltip with information about Y .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for tooltip of particular marker graph or null if marker graph is null.
+ public static DataTemplate GetYTooltipTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.DefaultTooltipTemplate;
+ }
+ ///
+ /// Gets template for marker tooltip with information about Y and .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for tooltip of particular marker graph or null if marker graph is null.
+ public static DataTemplate GetColorTooltipTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.ColorTooltip;
+ }
+ ///
+ /// Gets template for marker tooltip with information about Y and .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for tooltip of particular marker graph or null if marker graph is null.
+ public static DataTemplate GetSizeTooltipTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.SizeTooltip;
+ }
+ ///
+ /// Gets template for marker tooltip with information about Y , and .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for tooltip of particular marker graph or null if marker graph is null.
+ public static DataTemplate GetColorSizeTooltipTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.ColorSizeTooltip;
+ }
+ }
+ ///
+ /// Provides methods to get predefined templates for circle markers.
+ ///
+ public class CircleMarker : ColorSizeMarker
+ {
+ ///
+ /// Gets template for circle markers.
+ ///
+ /// Marker graph to get template for.
+ /// A template used for markers of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetMarkerTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.Circle;
+ }
+ ///
+ /// Gets template for circle marker legend with information about Y .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetYLegendTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.CircleLegend;
+ }
+ ///
+ /// Gets template for circle marker legend with information about Y and .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetColorLegendTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.CircleColorLegend;
+ }
+
+ ///
+ /// Gets template for circle marker legend with information about Y and .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetSizeLegendTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.CircleSizeLegend;
+ }
+
+ ///
+ /// Gets template for circle marker legend with information about Y , and .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetColorSizeLegendTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.CircleColorSizeLegend;
+ }
+ }
+ ///
+ /// Provides methods to get predefined templates for box markers.
+ ///
+ public class BoxMarker : ColorSizeMarker
+ {
+ ///
+ /// Gets template for box markers.
+ ///
+ /// Marker graph to get template for.
+ /// A template used for markers of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetMarkerTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.Box;
+ }
+
+ ///
+ /// Gets template for box marker legend with information about Y .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetYLegendTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.BoxLegend;
+ }
+
+ ///
+ /// Gets template for box marker legend with information about Y and .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetColorLegendTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.BoxColorLegend;
+ }
+
+ ///
+ /// Gets template for box marker legend with information about Y and .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetSizeLegendTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.BoxSizeLegend;
+ }
+
+ ///
+ /// Gets template for box marker legend with information about Y , and .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetColorSizeLegendTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.BoxColorSizeLegend;
+ }
+ }
+
+ ///
+ /// Provides methods to get predefined templates for cross markers.
+ ///
+ public class CrossMarker : ColorSizeMarker
+ {
+ ///
+ /// Gets template for cross markers.
+ ///
+ /// Marker graph to get template for.
+ /// A template used for markers of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetMarkerTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.Cross;
+ }
+
+ ///
+ /// Gets template for cross marker legend with information about Y .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetYLegendTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.CrossLegend;
+ }
+
+ ///
+ /// Gets template for cross marker legend with information about Y and .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetColorLegendTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.CrossColorLegend;
+ }
+
+ ///
+ /// Gets template for cross marker legend with information about Y and .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetSizeLegendTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.CrossSizeLegend;
+ }
+
+ ///
+ /// Gets template for cross marker legend with information about Y , and .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetColorSizeLegendTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.CrossColorSizeLegend;
+ }
+ }
+
+ ///
+ /// Provides methods to get predefined templates for diamond markers.
+ ///
+ public class DiamondMarker : ColorSizeMarker
+ {
+ ///
+ /// Gets template for diamond markers.
+ ///
+ /// Marker graph to get template for.
+ /// A template used for markers of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetMarkerTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.Diamond;
+ }
+
+ ///
+ /// Gets template for diamond marker legend with information about Y .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetYLegendTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.DiamondLegend;
+ }
+
+ ///
+ /// Gets template for diamond marker legend with information about Y and .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetColorLegendTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.DiamondColorLegend;
+ }
+
+ ///
+ /// Gets template for diamond marker legend with information about Y and .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetSizeLegendTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.DiamondSizeLegend;
+ }
+
+ ///
+ /// Gets template for diamond marker legend with information about Y , and .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetColorSizeLegendTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.DiamondColorSizeLegend;
+ }
+ }
+
+ ///
+ /// Provides methods to get predefined templates for triangle markers.
+ ///
+ public class TriangleMarker : ColorSizeMarker
+ {
+ ///
+ /// Gets template for triangle markers.
+ ///
+ /// Marker graph to get template for.
+ /// A template used for markers of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetMarkerTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.Triangle;
+ }
+
+ ///
+ /// Gets template for triangle marker legend with information about Y .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetYLegendTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.TriangleLegend;
+ }
+
+ ///
+ /// Gets template for triangle marker legend with information about Y and .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetColorLegendTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.TriangleColorLegend;
+ }
+
+ ///
+ /// Gets template for triangle marker legend with information about Y and .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetSizeLegendTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.TriangleSizeLegend;
+ }
+
+ ///
+ /// Gets template for triangle marker legend with information about Y , and .
+ ///
+ /// Marker graph to get template for.
+ /// A template used for legend of particular marker graph or null if marker graph is null.
+ public override DataTemplate GetColorSizeLegendTemplate(MarkerGraph mg)
+ {
+ if (mg == null)
+ return null;
+ return mg.TriangleColorSizeLegend;
+ }
+ }
+}
+
diff --git a/InteractiveDataDisplay_OneDll/src/Plots/Markers/DataSeries.cs b/InteractiveDataDisplay_OneDll/src/Plots/Markers/DataSeries.cs
new file mode 100644
index 000000000..0c28fc1a4
--- /dev/null
+++ b/InteractiveDataDisplay_OneDll/src/Plots/Markers/DataSeries.cs
@@ -0,0 +1,806 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Linq;
+using System.Windows;
+using System.Collections.Generic;
+using System.Windows.Data;
+using System.Collections.ObjectModel;
+using System.Windows.Media;
+using System.Collections;
+using System.Globalization;
+using System.Diagnostics;
+using System.ComponentModel;
+
+namespace InteractiveDataDisplay.WPF
+{
+ ///
+ /// Represents one data series (or variable).
+ ///
+ public class DataSeries : DependencyObject
+ {
+ ///
+ /// Occurs when a value of property was changed.
+ ///
+ public event EventHandler DataChanged;
+
+ private double minValue = Double.NaN;
+ private double maxValue = Double.NaN;
+ private Array cachedEnum; // Cached values from IEnumerable
+
+ ///
+ /// Gets or sets the unique key of data series. Data series is accessed by this key when
+ /// drawing markers and composing a legend.
+ ///
+ public string Key { get; set; }
+
+ ///
+ /// Gets or sets the readable description of this data series. It is shown in legend by default.
+ /// Default value is null.
+ ///
+ public string Description
+ {
+ get { return (string)GetValue(DescriptionProperty); }
+ set { SetValue(DescriptionProperty, (string)value); }
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty DescriptionProperty =
+ DependencyProperty.Register("Description", typeof(string), typeof(DataSeries),
+ new PropertyMetadata(null, OnDescriptionPropertyChanged));
+
+ private static void OnDescriptionPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
+ {
+ var ds = sender as DataSeries;
+ if (ds.DataChanged != null) ds.DataChanged(ds, new EventArgs());
+ }
+
+ ///
+ /// Gets or sets the data source for data series. This can be IEnumerable or 1D array for vector data or scalar objects.
+ /// Default value is null.
+ ///
+ public object Data
+ {
+ get { return GetValue(DataProperty); }
+ set { SetValue(DataProperty, value); }
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty DataProperty =
+ DependencyProperty.Register("Data", typeof(object), typeof(DataSeries),
+ new PropertyMetadata(OnDataPropertyChanged));
+
+ private static void OnDataPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
+ {
+ ((DataSeries)sender).Update();
+ }
+
+ ///
+ /// A method to raise event.
+ ///
+ protected void RaiseDataChanged()
+ {
+ if (DataChanged != null)
+ DataChanged(this, EventArgs.Empty);
+ }
+
+ ///
+ /// Forces this data series to be updated.
+ /// Is usually called if items of data array were changed, but reference to array itself remained the same.
+ ///
+ public void Update()
+ {
+ minValue = Double.NaN;
+ maxValue = Double.NaN;
+ cachedEnum = null;
+ First = FindFirstDataItem();
+
+ RaiseDataChanged();
+ }
+
+ ///
+ /// Gets the first value in data series.
+ /// Returns null if is null.
+ ///
+ public object First
+ {
+ get { return (object)GetValue(FirstProperty); }
+ internal set { SetValue(FirstProperty, value); }
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly DependencyProperty FirstProperty =
+ DependencyProperty.Register("First", typeof(object), typeof(DataSeries), new PropertyMetadata(null));
+
+ ///
+ /// Gets the first value in data series and converts it if is specified.
+ ///
+ /// Converted first value.
+ public object FindFirstDataItem()
+ {
+ object uc = GetFirstUnconvertedDataItem();
+ if (uc != null && Converter != null)
+ return Converter.Convert(uc, null, this, CultureInfo.InvariantCulture);
+ else
+ return uc;
+ }
+
+ ///
+ /// Converts the elements of an System.Collection.IEnumerable to System.Array.
+ ///
+ /// An instance of System.Collection.IEnumerable class.
+ /// An instance of System.Array class.
+ public static Array GetArrayFromEnumerable(IEnumerable enumerable)
+ {
+ return enumerable.Cast