71 Комити 5874b5cdae ... b753e56284

Аутор SHA1 Порука Датум
  Martin Edlund b753e56284 Cleaned up before pull request пре 5 година
  Martin Edlund 2ca10a6441 Added support for multiple debug lines пре 5 година
  Martin Edlund 7c008b4a09 Added functionality to display an overlay пре 5 година
  Martin Edlund 391be63b67 Fixed build error пре 5 година
  Martin Edlund 1a4d46cd93 Removed Functionality covered by Userstory 14 пре 5 година
  Martin Edlund a523f75bb4 Merge branch 'hotfix' into userstory13 пре 5 година
  Martin Edlund a8d56428f2 Removed build errors пре 5 година
  Martin Edlund 475e8cb1d9 Readded all tests пре 5 година
  Martin Edlund 7ec2c6566a Tried changing executable directory пре 5 година
  Martin Edlund 7296199724 screengrab try 3 пре 5 година
  Martin Edlund 4ef5d97e75 Screengrab try 2 пре 5 година
  Martin Edlund e348533c41 Added printscreen workaround пре 5 година
  Martin Edlund e2b4b6abc4 Change svg load test пре 5 година
  Martin Edlund d3bb456c65 Direct input tests пре 5 година
  Martin Edlund 4f3a515444 New IO Test (╯°□°)╯︵ ┻━┻ пре 5 година
  Martin Edlund 57db30ac32 Readded all tests ヽ(ー_ー )ノ пре 5 година
  Martin Edlund 70483ca432 Changed it to an undo test (个_个) пре 5 година
  Martin Edlund d21b08a341 Maybe fixed test ¯\_(ツ)_/¯ пре 5 година
  Martin Edlund 07c5eaea8b Changed test again пре 5 година
  Martin Edlund f1553ec331 Changed one test пре 5 година
  Martin Edlund 61c7a3dc2b Split tests into categories пре 5 година
  Martin Edlund e60c45c027 Added direct input tests пре 5 година
  Martin Edlund 5fa27e027c Added more tests пре 5 година
  Martin Edlund 57a82e21ee Testing new test пре 5 година
  Martin Edlund 223b22280f Testing IO test пре 5 година
  Martin Edlund ce12ae7197 AppVeyor Test пре 5 година
  Martin Edlund f9c074a78d Fixed build error пре 5 година
  Martin Edlund 448a9e6a4b Fixed target CPU error пре 5 година
  Martin Edlund f563232cce Cleaned up code and a user experience upgrade пре 5 година
  Martin Edlund 261cd978b8 cleaned up GUI and attempted to fix phantom line error пре 5 година
  m-edlund e93039223e Userstory8 (#14) пре 5 година
  Martin Edlund 4c6f436742 Added angle calculation пре 5 година
  Martin Edlund bfb7776d17 Slight changes пре 5 година
  Martin Edlund 951eede671 Started userstory 13 пре 5 година
  m-edlund 68f620d518 Userstory21 (#13) пре 5 година
  Rumei a229ef486e slight changes to program to suite codecoverage runs пре 5 година
  Vincenz Mechler b3e1f256e7 hotfix, added exception handling to isad import method call and added missing method comment to svg import ui method пре 5 година
  Vincenz Mechler 8e8ce61fb2 removed unnecessary files пре 5 година
  Vincenz Mechler 23ac7af994 excluded svg parsing tests from AppVeyor execution as they depend on filesystem access and the [DeploymentItem] attribute пре 5 година
  Vincenz Mechler 9e858f3e38 moved FileImporter test class to UITest.cs, adapted relative file paths пре 5 година
  Vincenz Mechler fb30fd704e merged develop into userstory2 in preperation for backward merging пре 5 година
  Vincenz Mechler e9ad799202 added comments to FileImporter tests пре 5 година
  chooga e6f3cbdba9 Update userstory6.md пре 5 година
  Martin Edlund 9988b5b6e0 Optimized InternalLine пре 5 година
  Vincenz Mechler c2b20ea890 finished basic tests for svg importer and added a .testsettings file пре 5 година
  Martin Edlund 46a6fe9e8e Hotfix + Cleaned up Project Folder пре 5 година
  m-edlund 28eaf0e7d2 Userstory16 (#11) пре 5 година
  Vincenz Mechler e24cc22479 added missing comments as requested by code review and added basic tests for error throwing behaviour of parseSVGInput method, although currently not working due to problems with deploying external files for testing пре 5 година
  Martin Edlund 2ff4f32409 Merge branch 'develop' into userstory2 пре 5 година
  Martin Edlund f754e5a215 Removed empty constructor пре 5 година
  Vincenz Mechler 45f8924dc4 removed dead code, mainly deactivated console prints, removed empty lines inside methods and fixed method naming scheme to always start with uppercase letter пре 5 година
  Vincenz Mechler 2fe93d8927 cleanup and added error for unterminated svg files пре 5 година
  Vincenz Mechler ae6c32073e fixed elliptical arc coordinate mismatch bug пре 5 година
  Vincenz Mechler 1302db3636 fixed elliptical arc rotation bug пре 5 година
  m-edlund 7737c197f0 Userstory19 (#9) пре 5 година
  Vincenz Mechler 7ade1eb4d2 fixed a few bugs пре 6 година
  Vincenz Mechler 1f90174628 implemented path data normalization, fixed a bug when parsing closepath path elements пре 6 година
  Vincenz Mechler 10d9bc9959 implemented svg path parsing (except for ellipse path elements (A and a)) пре 6 година
  m-edlund 2bb3e58115 Userstory 13 & 17 (#8) пре 6 година
  Martin Edlund 5874b5cdae Prepared for merging пре 6 година
  Martin Edlund d9409273d6 Adapted tests for new Tick function пре 6 година
  Martin Edlund f612e79911 Fixed bugs with the redraw feature пре 6 година
  Vincenz Mechler 541bd6657c added folder for test input files and created simple svgs пре 6 година
  Vincenz Mechler 1f0e044436 implemented ellipse sampling and deactivated path parsing for now пре 6 година
  Martin Edlund ecfb77a81b Added tests for the RedrawAssistant пре 6 година
  Vincenz Mechler e50cdc5fa8 started implementing svg parsing: implemented rect, line, polyline, polygon parsing, created circle, ellipse stubs, begun implementation of path parsing пре 6 година
  Martin Edlund c7d82c1de8 Added userstory 17 md file. пре 6 година
  Martin Edlund 4393adeeb2 Added working Redraw Functionality пре 6 година
  Martin Edlund 83b1ef645c Added functionality for marking line ends. пре 6 година
  Martin Edlund 37241850af Added Functions to calulate circles. пре 6 година
  Martin Edlund 910e89142c Minor Changes, bugfixes. Start US 13 пре 6 година
68 измењених фајлова са 6019 додато и 2498 уклоњено
  1. 27 0
      .appveyor.yml
  2. 0 6
      .travis.yml
  3. 8 8
      Finished Userstories/userstory13.md
  4. 13 0
      Finished Userstories/userstory15.md
  5. 13 0
      Finished Userstories/userstory16.md
  6. 13 0
      Finished Userstories/userstory18.md
  7. 13 0
      Finished Userstories/userstory19.md
  8. 13 0
      Finished Userstories/userstory21.md
  9. 2 2
      Finished Userstories/userstory6.md
  10. 13 0
      Finished Userstories/userstory8.md
  11. 4 1
      README.md
  12. 3 0
      SketchAssistant/.gitignore
  13. 25 0
      SketchAssistant/GenerateCoverageReport.bat
  14. 63 208
      SketchAssistant/SketchAssistant.Tests/UnitTest1.cs
  15. 0 6
      SketchAssistant/SketchAssistant.Tests/packages.config
  16. 15 10
      SketchAssistant/SketchAssistant.sln
  17. 0 14
      SketchAssistant/SketchAssistant/App.config
  18. 0 155
      SketchAssistant/SketchAssistant/FileImporter.cs
  19. 0 299
      SketchAssistant/SketchAssistant/Form1.Designer.cs
  20. 0 802
      SketchAssistant/SketchAssistant/Form1.cs
  21. 0 217
      SketchAssistant/SketchAssistant/Form1.resx
  22. 0 204
      SketchAssistant/SketchAssistant/GeometryCalculator.cs
  23. 0 152
      SketchAssistant/SketchAssistant/Line.cs
  24. 0 22
      SketchAssistant/SketchAssistant/Program.cs
  25. 0 36
      SketchAssistant/SketchAssistant/Properties/AssemblyInfo.cs
  26. 0 7
      SketchAssistant/SketchAssistant/Properties/Settings.settings
  27. 0 221
      SketchAssistant/SketchAssistant/RedrawAssistant.cs
  28. 0 6
      SketchAssistant/SketchAssistant/packages.config
  29. 24 17
      SketchAssistant/SketchAssistantWPF/ActionHistory.cs
  30. 6 0
      SketchAssistant/SketchAssistantWPF/App.config
  31. 9 0
      SketchAssistant/SketchAssistantWPF/App.xaml
  32. 17 0
      SketchAssistant/SketchAssistantWPF/App.xaml.cs
  33. 15 0
      SketchAssistant/SketchAssistantWPF/DebugData.cs
  34. 1685 0
      SketchAssistant/SketchAssistantWPF/FileImporter.cs
  35. 3 3
      SketchAssistant/SketchAssistantWPF/FileImporterException.cs
  36. 305 0
      SketchAssistant/SketchAssistantWPF/GeometryCalculator.cs
  37. 27 0
      SketchAssistant/SketchAssistantWPF/ImageDimension.cs
  38. 194 0
      SketchAssistant/SketchAssistantWPF/InternalLine.cs
  39. 422 0
      SketchAssistant/SketchAssistantWPF/MVP_Model.cs
  40. 526 0
      SketchAssistant/SketchAssistantWPF/MVP_Presenter.cs
  41. 133 0
      SketchAssistant/SketchAssistantWPF/MVP_View.cs
  42. 196 0
      SketchAssistant/SketchAssistantWPF/MainWindow.xaml
  43. 668 0
      SketchAssistant/SketchAssistantWPF/MainWindow.xaml.cs
  44. 55 0
      SketchAssistant/SketchAssistantWPF/Properties/AssemblyInfo.cs
  45. 20 28
      SketchAssistant/SketchAssistantWPF/Properties/Resources.Designer.cs
  46. 10 7
      SketchAssistant/SketchAssistantWPF/Properties/Resources.resx
  47. 2 2
      SketchAssistant/SketchAssistantWPF/Properties/Settings.Designer.cs
  48. 7 0
      SketchAssistant/SketchAssistantWPF/Properties/Settings.settings
  49. 2 5
      SketchAssistant/SketchAssistantWPF/SketchAction.cs
  50. 69 44
      SketchAssistant/SketchAssistantWPF/SketchAssistantWPF.csproj
  51. BIN
      SketchAssistant/SketchAssistantWPF/nicubunu-Quill.ico
  52. 6 0
      SketchAssistant/SketchAssistantWPF/packages.config
  53. 4 4
      SketchAssistant/WhiteTests/Properties/AssemblyInfo.cs
  54. 1012 0
      SketchAssistant/WhiteTests/UITest.cs
  55. 60 10
      SketchAssistant/WhiteTests/WhiteTests.csproj
  56. 2 2
      SketchAssistant/WhiteTests/app.config
  57. 11 0
      SketchAssistant/WhiteTests/packages.config
  58. 10 0
      SketchAssistant/WhiteTests/test.runsettings
  59. 30 0
      SketchAssistant/WhiteTests/test_input_files/blacklisted/all_no_size_def_in_header.svg
  60. 29 0
      SketchAssistant/WhiteTests/test_input_files/blacklisted/all_unclosed.svg
  61. 30 0
      SketchAssistant/WhiteTests/test_input_files/whitelisted/all.svg
  62. 14 0
      SketchAssistant/WhiteTests/test_input_files/whitelisted/circle_ellipse.svg
  63. 10 0
      SketchAssistant/WhiteTests/test_input_files/whitelisted/line.svg
  64. 21 0
      SketchAssistant/WhiteTests/test_input_files/whitelisted/path.svg
  65. 10 0
      SketchAssistant/WhiteTests/test_input_files/whitelisted/polygon.svg
  66. 11 0
      SketchAssistant/WhiteTests/test_input_files/whitelisted/polyline.svg
  67. 13 0
      SketchAssistant/WhiteTests/test_input_files/whitelisted/rect.svg
  68. 126 0
      screenres.ps1

+ 27 - 0
.appveyor.yml

@@ -0,0 +1,27 @@
+image:
+ - Visual Studio 2017
+ 
+install:
+ - "nuget install TestStack.White"
+ - "nuget install MSTest.TestAdapter"
+ - "nuget install MSTest.TestFramework"
+ - "nuget restore SketchAssistant/SketchAssistant.sln"
+ 
+before_build:
+ - ps: .\screenres.ps1
+ 
+artifacts:
+ - path: \SketchAssistant\GeneratedReports\
+#  - path: SketchAssistant/WhiteTests/test_input_files/whitelisted/*.svg
+#    name: whitelisted svg files for testing
+#  - path: SketchAssistant/WhiteTests/test_input_files/blacklisted/*.svg
+#    name: blacklisted svg files for testing
+
+before_test:
+ - ps: "SketchAssistant/GenerateCoverageReport.bat"
+
+#test:
+    #don't run tests depending on [DeploymentItem] and filesystem access
+    #categories:
+    #    except:
+    #        - FileIO

+ 0 - 6
.travis.yml

@@ -1,6 +0,0 @@
-language: csharp
-solution: "./SketchAssistant/SketchAssistant.sln"
-
-install:
-  - nuget restore "./SketchAssistant/SketchAssistant.sln"
-  - nuget install NUnit.ConsoleRunner -Version 3.9.0 -OutputDirectory testrunner

+ 8 - 8
Finished Userstories/userstory13.md

@@ -1,13 +1,13 @@
-# Userstory 13 
+# Userstory 13 
  
 |**ID**|13|  
 |-|-|
-|**Name**|Start und Endpunkt anzeigen|
-|**Beschreibung**|Beim Nachzeichnen kann der Start- & Endpunkt einer nachzuzeichnenden Linie angzeigt werden.|
-|**Akzeptanzkriterium**|Sofern auf der linken Seite eine nachzeichenbare Grafik dargestellt wird, kann der Start- und Endpunkt der ersten Linie der linken Grafik an der richtigen Stelle auf der rechten Seite angezeigt werden.|
+|**Name**|Overlay Unterstützung|
+|**Beschreibung**|Als Nutzer möchte ich, dass beim Nachzeichnen, der Start- & Endpunkt einer nachzuzeichnenden Linie angzeigt werden kann, sowie bei Verwendung des Optitrack Systems ein Cursor angezeigt werden kann.|
+|**Akzeptanzkriterium**|Es ist möglich Overlay Elemente anzuzeigen und frei zu positionieren.|
 |Geschätzter Aufwand (Story Points)|2|
-|Entwickler|Martin|
-|Umgesetzt in Iteration|4|
-|Tatsächlicher Aufwand (Std.)|1|
-|Velocity (Std./Story Point)|0.5|
+|Entwickler|Martin Edlund|
+|Umgesetzt in Iteration|15|
+|Tatsächlicher Aufwand (Std.)|8|
+|Velocity (Std./Story Point)|4|
 |Bemerkungen|Keine|

+ 13 - 0
Finished Userstories/userstory15.md

@@ -0,0 +1,13 @@
+# Userstory 15  
+ 
+|**ID**|15|  
+|-|-|
+|**Name**|Schöne Linien|
+|**Beschreibung**|Als Auftraggeber wünsche ich, dass die Linien die gezeichnet werden, an den Enden dünner und in der Mitte dicker sind, um einem echten Stift zu ähneln.|
+|**Akzeptanzkriterium**|Die Userstory wird akzeptiert, wenn die vom Anwender gezeichnete Line am Anfang schmaler ist, im Verlauf etwas breiter wird und am Ende wieder so schmal wie am Anfang ist.|
+|Geschätzter Aufwand (Story Points)|10|
+|Entwickler|Tim|
+|Umgesetzt in Iteration|9|
+|Tatsächlicher Aufwand (Std.)|keine|
+|Velocity (Std./Story Point)|keine|
+|Bemerkungen|Wurde durch Userstory 16 umgesetzt|

+ 13 - 0
Finished Userstories/userstory16.md

@@ -0,0 +1,13 @@
+# Userstory 16  
+ 
+|**ID**|16|  
+|-|-|
+|**Name**|Änderung der Nutzeroberfläche & wpf|
+|**Beschreibung**|Die Nutzeroberfläche wird an die neuen Anforderungen des Auftraggebers angepasst.|
+|**Akzeptanzkriterium**|Die Userstory ist akzeptiert, wenn die UI eine Zeichenfläche enthält und eine Fläche zum Anzeigen von Grafiken, sowie alle vorherig verfügbare Knöpfe. Zusätzlich ist erforderlich, dass die UI statt auf WinForms auf WPF basiert. Auch sollen die Knöpfe in der Toolbar durch Symbole, statt durch Text markiert sein, und es einen Edit Button gibt in welchem zusätzlich die Funktionen vorhanden sind.|
+|Geschätzter Aufwand (Story Points)|10|
+|Entwickler|Martin Edlund|
+|Umgesetzt in Iteration|9|
+|Tatsächlicher Aufwand (Std.)|30|
+|Velocity (Std./Story Point)|3|
+|Bemerkungen|Keine|

+ 13 - 0
Finished Userstories/userstory18.md

@@ -0,0 +1,13 @@
+# Userstory 18  
+ 
+|**ID**|18|  
+|-|-|
+|**Name**|Antialiasing|
+|**Beschreibung**|Als Auftraggeber möchte ich eine möglichst saubere Linie auf dem Bildschrim sehen, das heißt, dass die Kanten der Pixel geglättet werden.|
+|**Akzeptanzkriterium**|Die Breite der Linie wird an die Größe des Bildes in der rechten Picturebox angepasst. Zudem soll Antialiasing auf die Linien angewendet werden, damit diese glatte Kanten aufweisen.|
+|Geschätzter Aufwand (Story Points)|keine|
+|Entwickler|keiner|
+|Umgesetzt in Iteration|9|
+|Tatsächlicher Aufwand (Std.)|keine|
+|Velocity (Std./Story Point)|keine|
+|Bemerkungen|Wurde durch Userstory 16 gelöst|

+ 13 - 0
Finished Userstories/userstory19.md

@@ -0,0 +1,13 @@
+# Userstory 19  
+ 
+|**ID**|19|  
+|-|-|
+|**Name**|Verbesserung der Struktur des Programms|
+|**Beschreibung**|Strukturierung des Programms zu einem Model-View-Controller Modell.|
+|**Akzeptanzkriterium**|Aufteilung der Form Datei in drei Klassen: Eine View Klasse, welche Aufgaben bezüglich Anzeigen im Form übernimmt. Eine Controller Klasse, welche Eingaben vom Form empfängt und verarbeitet. Sowie eine Model Klasse, welche den Status des Programms darstellt, und vom Controller manipuliert wird und selbst den View anpasst.|
+|Geschätzter Aufwand (Story Points)|20|
+|Entwickler|Martin Edlund|
+|Umgesetzt in Iteration|6|
+|Tatsächlicher Aufwand (Std.)|8,5|
+|Velocity (Std./Story Point)|0,425|
+|Bemerkungen|Keine|

+ 13 - 0
Finished Userstories/userstory21.md

@@ -0,0 +1,13 @@
+# Userstory 21  
+ 
+|**ID**|21|  
+|-|-|
+|**Name**|InkCanvas|
+|**Beschreibung**|Bei den manuellen Tests haben sich beim zeichnen auserhalb der vorgesehnen Zeichenoberfläche Fehler ergeben. Als Programmierer benötige ich eine Oberfläche, die resistenter gegen solche Fehler ist.|
+|**Akzeptanzkriterium**|InkCanvas soll verwendet werden, um die aktuelle Linie zu zeichnen und anzuzeigen.|
+|Geschätzter Aufwand (Story Points)|5|
+|Entwickler|Tim Reischl|
+|Umgesetzt in Iteration|13|
+|Tatsächlicher Aufwand (Std.)|30|
+|Velocity (Std./Story Point)|6|
+|Bemerkungen|Keine|

+ 2 - 2
Finished Userstories/userstory6.md

@@ -4,10 +4,10 @@
 |-|-|
 |**Name**|Löschfunktionalität|
 |**Beschreibung**|Man kann einen Knopf in der UI drücken um in den Löschmodus zu kommen, und um wieder aus dem Löschmodus rauszukommen. Wenn man im Löschmodus ist, kann man auf einer Leinwand im rechten Bereich gezeichnete Elemente löschen indem man auf sie klickt.|
-|**Akzeptanzkriterium**|Der Knopf für den Löschmodus kann nur gedrückt werden, wenn im rechten Bereich eine Zeichennleinwand vorhanden ist. Wenn man den Knopf drückt ohne im Löschmodus zu sein, verändert sich der Knopf erkennbar und man kommt in den Löschmodus. Drückt man den Knopf, während man im Löschmodus ist, wird der Löschmodus beendet und der Knopf bekommt wieder sein urspüngliches Aussehen. Während man im Löschmodus ist, kann man auf der Leinwand im rechten Bereich Elemente löschen, wenn der Mauszeiger sie berührt während man die Maustaste gedrückt hält. Die Elemente die man löscht verschwinden. Drückt man den Löschknopf während man in einem anderen Modus ist (z.B. Zeichenmodus) wird der andere Modus beendet und man ist im Löschmodus.|
+|**Akzeptanzkriterium**|Der Knopf für den Löschmodus kann nur gedrückt werden, wenn im rechten Bereich eine Zeichennleinwand vorhanden ist. Wenn man den Knopf drückt ohne im Löschmodus zu sein, verändert sich der Knopf erkennbar und man kommt in den Löschmodus. Drückt man den Knopf, während man im Löschmodus ist, bleibt der Knopf gleich und man ist immer noch im Löschmodus. Während man im Löschmodus ist, kann man auf der Leinwand im rechten Bereich Elemente löschen, wenn der Mauszeiger sie berührt während man die Maustaste gedrückt hält. Die Elemente die man löscht verschwinden. Drückt man den Löschknopf während man in einem anderen Modus ist (z.B. Zeichenmodus) wird der andere Modus beendet und man ist im Löschmodus.|
 |Geschätzter Aufwand (Story Points)|16|
 |Entwickler|Martin Edlund, Tim Reischl|
 |Umgesetzt in Iteration|3|
 |Tatsächlicher Aufwand (Std.)|14,5|
 |Velocity (Std./Story Point)|0,90625|
-|Bemerkungen|Keine|
+|Bemerkungen|Keine|

+ 13 - 0
Finished Userstories/userstory8.md

@@ -0,0 +1,13 @@
+# Userstory 8
+
+|**ID**|8|
+|-|-|
+|**Name**|Vergleich vom gezeichneten Bild mit der Ursprungsgrafik|
+|**Beschreibung**|Ein Feature, das die vom Nutzer gezeichnete Grafik, mit der geladenen Grafik vergleicht.|
+|**Akzeptanzkriterium**|Eine Funktion bei der Ähnlichkeit der vom Nutzer gezeichneten Zeichnung und der geladenen Grafik in der UI angezeigt wird.|
+|Geschätzter Aufwand (Story Points)|10|
+|Entwickler|Martin Edlund|
+|Umgesetzt in Iteration|14|
+|Tatsächlicher Aufwand (Std.)|12.5|
+|Velocity (Std./Story Point)|1.25|
+|Bemerkungen|Keine|

+ 4 - 1
README.md

@@ -1 +1,4 @@
-# BP-SketchAssistant
+# Interactive Sketch Assistant
+[![Build status](https://ci.appveyor.com/api/projects/status/yvefugnjqwc8649y?svg=true)](https://ci.appveyor.com/project/m-edlund/bp-sketchassistant)
+
+A Sketch Assistant to help you with sketching

+ 3 - 0
SketchAssistant/.gitignore

@@ -0,0 +1,3 @@
+/GeneratedReports/
+!*.ico
+!*.svg

+ 25 - 0
SketchAssistant/GenerateCoverageReport.bat

@@ -0,0 +1,25 @@
+if not exist "%~dp0GeneratedReports" mkdir "%~dp0GeneratedReports"
+
+for /d %%a in (
+  "%~dp0\packages\OpenCover.*"
+) do set "openCoverFolder=%%~fa\"
+
+for /d %%a in (
+  "%~dp0\packages\Microsoft.TestPlatform.*"
+) do set "microPlat=%%~fa\"
+
+for /d %%a in (
+  "%~dp0\packages\ReportGenerator.*"
+) do set "repGen=%%~fa\"
+
+"%openCoverFolder%\tools\OpenCover.Console.exe" ^
+-register:user ^
+-target:"%microPlat%\tools\net451\Common7\IDE\Extensions\TestPlatform\vstest.console.exe" ^
+-targetargs:"%~dp0\WhiteTests\bin\Debug\WhiteTests.dll" ^
+-filter:"+[SketchAssistantWPF*]*" ^
+-mergebyhash ^
+-output:"%~dp0\GeneratedReports\opencovertests.xml"
+
+"%repGen%\tools\net47\ReportGenerator.exe" ^
+-reports:"%~dp0\GeneratedReports\opencovertests.xml" ^
+-targetdir:"%~dp0\GeneratedReports\ReportGeneratorOutput"

+ 63 - 208
SketchAssistant/SketchAssistant.Tests/UnitTest1.cs

@@ -4,6 +4,7 @@ using System.Drawing;
 using System.Collections.Generic;
 using SketchAssistant;
 using System.Windows.Forms;
+using System.IO;
 
 namespace Tests
 {
@@ -205,11 +206,10 @@ namespace Tests
     [TestClass]
     public class ActionHistoryTests
     {
-        ToolStripStatusLabel testLabel = new ToolStripStatusLabel();
 
         private ActionHistory GetActionHistory()
         {
-            return new ActionHistory(testLabel);
+            return new ActionHistory();
         }
 
         [DataTestMethod]
@@ -267,16 +267,37 @@ namespace Tests
             Assert.AreEqual(true, testHistory.CanUndo());
             testHistory.MoveAction(true);
             Assert.AreEqual(true, testHistory.CanRedo());
-            testHistory.MoveAction(false);
+            var lastActionLabel = testHistory.MoveAction(false);
             Assert.AreEqual(actionType, testHistory.GetCurrentAction().GetActionType());
-            String currLabel = testLabel.Text;
-            Assert.AreEqual(currLabel, message);
+            Assert.AreEqual(message, lastActionLabel);
         }
     }
 
     [TestClass]
+    [DeploymentItem(@"SketchAssistant.Tests\test_input_files\")]
     public class FileImporterTests
-    {
+    { 
+
+        /// <summary>
+        /// instance of TestContext to be able to access deployed files
+        /// </summary>
+         private TestContext testContextInstance;
+        /// <summary>
+        ///Gets or sets the test context which provides
+        ///information about and functionality for the current test run.
+        ///</summary>
+        public TestContext TestContext
+        {
+            get
+            {
+                return testContextInstance;
+            }
+            set
+            {
+                testContextInstance = value;
+            }
+        }
+        /*
         [DataTestMethod]
         [DataRow(new int[] { 54, 43, 57, 11, 145, 34, 113, 299, 0 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 })]
         [DataRow(new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 })]
@@ -284,7 +305,7 @@ namespace Tests
         public void ParseISADInputSuccessfulTest(int[] xCoordinates, int[] yCoordinates)
         {
             Form1 program = new Form1();
-            FileImporter uut = new SketchAssistant.FileImporter(program);
+            FileImporter uut = new SketchAssistant.FileImporter();
 
             List<String> file = new List<string>();
             file.Add("drawing");
@@ -335,7 +356,7 @@ namespace Tests
         {
             bool exceptionThrown = false;
             Form1 program = new Form1();
-            FileImporter uut = new SketchAssistant.FileImporter(program);
+            FileImporter uut = new SketchAssistant.FileImporter();
             //check that left image initially is uninitialized
             Assert.IsNull(GetLeftImage(program));
             //initialize left image with a valid isad drawing
@@ -367,229 +388,63 @@ namespace Tests
         private List<Line> GetLeftImage(Form1 program)
         {
             //cast is save as long as Form1#GetAllVariables() is conform to its contract
-            return (List<Line>)program.GetAllVariables().Find(x => x.Item1.Equals("leftLineList")).Item2;
-        }
-    }
-
-    [TestClass]
-    public class RedrawAssistantTests
-    {
-        private RedrawAssistant GetAssistant(List<Line> input)
-        {
-            if(input.Count == 0)
-            {
-                return new RedrawAssistant();
-            }
-            else
-            {
-                return new RedrawAssistant(input);
-            }
-        }
-
-        [DataTestMethod]
-        [DataRow(17, 20, new int[] { 54, 43, 57, 11, 145, 34, 113, 299, 0 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, true)]
-        [DataRow(-50, 30, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, false)]
-        [DataRow(-70, -20, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 54, 43, 57, 11, 145, 34, 113, 199, 0 }, true)]
-        public void InactiveRedrawAssistantTest(int x, int y, int[] xCoords, int[] yCoords, bool lineActive)
-        {
-            RedrawAssistant testAssistant = GetAssistant(new List<Line>());
-            List<Point> testPoints = new List<Point>();
-            for (int i = 0; i < xCoords.Length; i++)
-            {
-                testPoints.Add(new Point(xCoords[i], yCoords[i]));
-            }
-            List<Tuple<bool, Line>> testLines = new List<Tuple<bool, Line>> { new Tuple<bool, Line>(lineActive, new Line(testPoints, 0)) };
-            List<HashSet<Point>> result = testAssistant.Tick(new Point(1, 1), testLines, -1, false);
-            Assert.AreEqual(0, result.Count);
-        }
-
-        [DataTestMethod]
-        [DataRow(17, 20, new int[] { 54, 43, 57, 11, 145, 34, 113, 299, 0 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, 1)]
-        [DataRow(-50, 30, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, 1)]
-        [DataRow(33, 54, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 54, 43, 57, 11, 145, 34, 113, 199, 0 }, 2)]
-        public void ActiveRedrawAssistantTestStartedDrawing(int x, int y, int[] xCoords, int[] yCoords, int resultingCount)
-        {
-            List<Point> testPoints = new List<Point>();
-            for (int i = 0; i < xCoords.Length; i++)
-            {
-                testPoints.Add(new Point(xCoords[i], yCoords[i]));
-            }
-            List<Tuple<bool, Line>> testInputLines = new List<Tuple<bool, Line>>();
-            List<Line> testRedrawLines = new List<Line> { new Line(testPoints, 0) };
-            RedrawAssistant testAssistant = GetAssistant(testRedrawLines);
-            //Setting Marker Radius to 1 so that only the functionality of the RedrawAssistant is checked 
-            //and not the functionality of the Circle algorithm.
-            testAssistant.SetMarkerRadius(1);
-            List<HashSet<Point>> tickResult = testAssistant.Tick(new Point(x, y), testInputLines, -1, false);
-
-
-            Assert.AreEqual(resultingCount, tickResult.Count);
-            if(resultingCount == 1)
-            {
-                foreach(Point p in tickResult[0])
-                {
-                    Assert.AreEqual(testPoints[0], p);
-                }
-            }
-            if(resultingCount == 2)
-            {
-                foreach (Point p in tickResult[0])
-                {
-                    Assert.AreEqual(testPoints[0], p);
-                }
-                foreach (Point p in tickResult[1])
-                {
-                    Assert.AreEqual(testPoints[testPoints.Count-1], p);
-                }
-            }
+            return (List<Line>) program.GetAllVariables()[4];
         }
+        */
 
-        [DataTestMethod]
-        [DataRow(17, 20, new int[] { 54, 43, 57, 11, 145, 34, 113, 299, 0 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 },
-             new int[] { 77, 20, 3, 74, 28 }, new int[] { 40, 50, 20, 77, 28}, 2, false)]
-        [DataRow(33, 33, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 },
-             new int[] { 42, 140, 30, 30, 30 }, new int[] { 11, 145, 34, 113, 28 }, 2, true)]
-        [DataRow(33, 54, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 54, 43, 57, 11, 145, 34, 113, 199, 0 },
-             new int[] { 43, 57, 11, 145 }, new int[] { 33, 42, 140, 30 }, 2, true)]
-        public void ActiveRedrawAssistantTestMultipleLinesRedrawn(
-            int x, int y, int[] xCoords_one, int[] yCoords_one, int[] xCoords_two, int[] yCoords_two, int resultingCount, bool showingStartAndEnd)
+        /// <summary>
+        /// parses all whitelisted files and ensures no exceptions are thrown (parsing abortion, e.g. due to corrupted input files, are realized by throwing a FileImporterException)
+        /// </summary>
+        [TestMethod]
+        public void parseSVGInputNoErrorForWhitelistedFilesTest()
         {
-            List<Point> testPoints1 = new List<Point>();
-            for (int i = 0; i < xCoords_one.Length; i++)
-            {
-                testPoints1.Add(new Point(xCoords_one[i], yCoords_one[i]));
-            }
-            List<Point> testPoints2 = new List<Point>();
-            for (int i = 0; i < xCoords_two.Length; i++)
-            {
-                testPoints2.Add(new Point(xCoords_two[i], yCoords_two[i]));
-            }
-            List<Tuple<bool, Line>> testInputLines = new List<Tuple<bool, Line>>();
-
-            List<Line> testRedrawLines = new List<Line> { new Line(testPoints1, 0) , new Line(testPoints2, 1) };
-            RedrawAssistant testAssistant = GetAssistant(testRedrawLines);
-            //Setting Marker Radius to 1 so that only the functionality of the RedrawAssistant is checked 
-            //and not the functionality of the Circle algorithm.
-            testAssistant.SetMarkerRadius(1);
+            FileImporter uut = new FileImporter();
 
-            List<HashSet<Point>> tickResult = testAssistant.Tick(new Point(x, y), testInputLines, -1, false);
+            string[] files = Directory.GetFiles(TestContext.DeploymentDirectory + @"\test_input_files\whitelisted", "*.svg", SearchOption.AllDirectories);
+            Assert.IsTrue(files.Length > 0);
 
-            Assert.AreEqual(resultingCount, tickResult.Count);
-            if (showingStartAndEnd)
-            {
-                foreach (Point p in tickResult[0])
-                {
-                    Assert.AreEqual(testPoints1[0], p);
-                }
-                foreach (Point p in tickResult[1])
-                {
-                    Assert.AreEqual(testPoints1[testPoints1.Count - 1], p);
-                }
-            }
-            else
+            foreach (string s in files) //parse each of the whitelisted files
             {
-                foreach (Point p in tickResult[0])
+                bool noExceptionThrown = true;
+                try
                 {
-                    Assert.AreEqual(testPoints1[0], p);
+                    uut.ParseSVGInputFile(s, 10000, 10000);
                 }
-                foreach (Point p in tickResult[1])
+                catch (Exception e)
                 {
-                    Assert.AreEqual(testPoints2[0], p);
+                    noExceptionThrown = false;
                 }
+                Assert.IsTrue(noExceptionThrown);
             }
         }
 
-        [DataTestMethod]
-        [DataRow(17, 20, 17, 20, -1 ,false, new int[] { 54, 43, 57, 11, 145, 34, 113, 299, 0 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 },
-     new int[] { 77, 20, 3, 74, 28 }, new int[] { 40, 50, 20, 77, 28 }, 2, 2, false, false)]
-
-        [DataRow(33, 33, 2, 2, 0, true, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 },
-     new int[] { 42, 140, 30, 30, 30 }, new int[] { 11, 145, 34, 113, 28 }, 2, 1, true, false)]
-
-        [DataRow(33, 54, 17, 0, 0, true, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 54, 43, 57, 11, 145, 34, 113, 199, 0 },
-     new int[] { 43, 57, 11, 145 }, new int[] { 33, 42, 140, 30 }, 2, 2, true, false)]
-        public void ActiveRedrawAssistantTestLineFinished(
-            int x_one, int y_one, int x_two, int y_two, int lineID, bool finishedDrawing, int[] xCoords_one, int[] yCoords_one, int[] xCoords_two, int[] yCoords_two, 
-            int count_one, int count_two, bool showingSE_one, bool showingSE_two)
+        /// <summary>
+        /// parses all blacklisted files and ensures an instance of FileIporterException is thrown for each file, but no other exceptions occur
+        /// </summary>
+        [TestMethod]
+        public void parseSVGInputNoErrorForBlacklistedFilesTest()
         {
-            List<Point> testPoints1 = new List<Point>();
-            for (int i = 0; i < xCoords_one.Length; i++)
-            {
-                testPoints1.Add(new Point(xCoords_one[i], yCoords_one[i]));
-            }
-            List<Point> testPoints2 = new List<Point>();
-            for (int i = 0; i < xCoords_two.Length; i++)
-            {
-                testPoints2.Add(new Point(xCoords_two[i], yCoords_two[i]));
-            }
-            List<Tuple<bool, Line>> testInputLines = new List<Tuple<bool, Line>>();
-
-            List<Line> testRedrawLines = new List<Line> { new Line(testPoints1, 0), new Line(testPoints2, 1) };
-            RedrawAssistant testAssistant = GetAssistant(testRedrawLines);
-            //Setting Marker Radius to 1 so that only the functionality of the RedrawAssistant is checked 
-            //and not the functionality of the Circle algorithm.
-            testAssistant.SetMarkerRadius(1);
-
-            List<HashSet<Point>> tickResult1 = testAssistant.Tick(new Point(x_one, y_one), testInputLines, lineID, false);
-            List<HashSet<Point>> tickResult2 = testAssistant.Tick(new Point(x_two, y_two), testInputLines, lineID, finishedDrawing);
+            FileImporter uut = new FileImporter();
 
-            Assert.AreEqual(count_one, tickResult1.Count);
-            if (showingSE_one)
+            string[] files = Directory.GetFiles(TestContext.DeploymentDirectory + @"\test_input_files\blacklisted", "*.svg", SearchOption.AllDirectories);
+            Assert.IsTrue(files.Length > 0);
+            foreach (string s in files) //parse each of the blacklisted files
             {
-                foreach(Point p in tickResult1[0])
+                bool correctExceptionThrown = false;
+                try
                 {
-                    Assert.AreEqual(testPoints1[0], p);
+                    uut.ParseSVGInputFile(s, 10000, 10000);
                 }
-                foreach (Point p in tickResult1[1])
+                catch(FileImporterException e)
                 {
-                    Assert.AreEqual(testPoints1[testPoints1.Count - 1], p);
+                    correctExceptionThrown = true;
                 }
-            }
-            else
-            {
-                foreach (Point p in tickResult1[0])
-                {
-                    Assert.AreEqual(testPoints1[0], p);
-                }
-                foreach (Point p in tickResult1[1])
-                {
-                    Assert.AreEqual(testPoints2[0], p);
-                }
-            }
-
-            Assert.AreEqual(count_two, tickResult2.Count);
-            if(count_two == 2)
-            {
-                if (showingSE_two)
-                {
-                    foreach (Point p in tickResult2[0])
-                    {
-                        Assert.AreEqual(testPoints1[0], p);
-                    }
-                    foreach (Point p in tickResult2[1])
-                    {
-                        Assert.AreEqual(testPoints1[testPoints1.Count - 1], p);
-                    }
-                }
-                else
-                {
-                    foreach (Point p in tickResult2[0])
-                    {
-                        Assert.AreEqual(testPoints1[0], p);
-                    }
-                    foreach (Point p in tickResult2[1])
-                    {
-                        Assert.AreEqual(testPoints2[0], p);
-                    }
-                }
-            }
-            if(count_two == 1)
-            {
-                foreach (Point p in tickResult2[0])
+                catch(Exception e)
                 {
-                    Assert.AreEqual(testPoints2[0], p);
                 }
+                Assert.IsTrue(correctExceptionThrown);
             }
         }
     }
+
 }

+ 0 - 6
SketchAssistant/SketchAssistant.Tests/packages.config

@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
-  <package id="MSTest.TestAdapter" version="1.4.0" targetFramework="net461" />
-  <package id="MSTest.TestFramework" version="1.4.0" targetFramework="net461" />
-  <package id="System.ValueTuple" version="4.5.0" targetFramework="net461" />
-</packages>

+ 15 - 10
SketchAssistant/SketchAssistant.sln

@@ -3,9 +3,14 @@ Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio 15
 VisualStudioVersion = 15.0.28010.2050
 MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SketchAssistant", "SketchAssistant\SketchAssistant.csproj", "{0336F628-A2F7-4170-8B2E-9277C23118D4}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SketchAssistantWPF", "SketchAssistantWPF\SketchAssistantWPF.csproj", "{EE53AE79-2AA0-4F43-9638-1789B189D5C3}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SketchAssistant.Tests", "SketchAssistant.Tests\SketchAssistant.Tests.csproj", "{7DCDC31A-8291-4B05-93D6-DCC5DE27A4A0}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WhiteTests", "WhiteTests\WhiteTests.csproj", "{EB09C624-91F2-465F-825B-559BF7A7D5CB}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CoverageTools", "CoverageTools", "{A179F8A5-FBEB-4DBB-8AF4-DD1B5C714A8F}"
+	ProjectSection(SolutionItems) = preProject
+		GenerateCoverageReport.bat = GenerateCoverageReport.bat
+	EndProjectSection
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -13,14 +18,14 @@ Global
 		Release|Any CPU = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{0336F628-A2F7-4170-8B2E-9277C23118D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{0336F628-A2F7-4170-8B2E-9277C23118D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{0336F628-A2F7-4170-8B2E-9277C23118D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{0336F628-A2F7-4170-8B2E-9277C23118D4}.Release|Any CPU.Build.0 = Release|Any CPU
-		{7DCDC31A-8291-4B05-93D6-DCC5DE27A4A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{7DCDC31A-8291-4B05-93D6-DCC5DE27A4A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{7DCDC31A-8291-4B05-93D6-DCC5DE27A4A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{7DCDC31A-8291-4B05-93D6-DCC5DE27A4A0}.Release|Any CPU.Build.0 = Release|Any CPU
+		{EE53AE79-2AA0-4F43-9638-1789B189D5C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{EE53AE79-2AA0-4F43-9638-1789B189D5C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{EE53AE79-2AA0-4F43-9638-1789B189D5C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{EE53AE79-2AA0-4F43-9638-1789B189D5C3}.Release|Any CPU.Build.0 = Release|Any CPU
+		{EB09C624-91F2-465F-825B-559BF7A7D5CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{EB09C624-91F2-465F-825B-559BF7A7D5CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{EB09C624-91F2-465F-825B-559BF7A7D5CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{EB09C624-91F2-465F-825B-559BF7A7D5CB}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 0 - 14
SketchAssistant/SketchAssistant/App.config

@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<configuration>
-    <startup> 
-        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
-    </startup>
-  <runtime>
-    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
-      <dependentAssembly>
-        <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
-        <bindingRedirect oldVersion="0.0.0.0-4.0.4.1" newVersion="4.0.4.1" />
-      </dependentAssembly>
-    </assemblyBinding>
-  </runtime>
-</configuration>

+ 0 - 155
SketchAssistant/SketchAssistant/FileImporter.cs

@@ -1,155 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Threading.Tasks;
-
-namespace SketchAssistant
-{
-    public class FileImporter
-    {
-
-        /// <summary>
-        /// pointer to the running instance of main program
-        /// </summary>
-        Form1 program;
-
-        public FileImporter(Form1 newProgram)
-        {
-            program = newProgram;
-        }
-
-        /// <summary>
-        /// parses a drawing consisting of line objects, given as a file in the application specific .isad format
-        /// </summary>
-        /// <param name="fileName">the path of the input file</param>
-        /// <returns>the width and height of the left canvas and the parsed picture as a list of lines</returns>
-        public (int, int, List<Line>) ParseISADInputFile(String fileName)
-        {
-            return ParseISADInput(System.IO.File.ReadAllLines(fileName));
-        }
-
-        /// <summary>
-        /// parses a drawing consisting of line objects, given as the content of a .isad file, seperated into lines
-        /// </summary>
-        /// <param name="allLines">an array holding all lines of the input file</param>
-        /// <returns>the width and height of the left canvas and the parsed picture as a list of lines</returns>
-        private (int, int, List<Line>) ParseISADInput(String[] allLines)
-        {
-
-            if (allLines.Length == 0)
-            {
-                throw new FileImporterException("file is empty", "", -1);
-            }
-            if (!"drawing".Equals(allLines[0]))
-            {
-                throw new FileImporterException("file is not an interactive sketch assistant drawing", ".isad files have to start with the 'drawing' token", 1);
-            }
-            if (!"enddrawing".Equals(allLines[allLines.Length - 1]))
-            {
-                throw new FileImporterException("unterminated drawing definition", ".isad files have to end with the 'enddrawing' token", allLines.Length);
-            }
-
-            (int, int) dimensions = ParseISADHeader(allLines);
-            List<Line> picture = ParseISADBody(allLines, dimensions.Item1, dimensions.Item2);
-
-            return (dimensions.Item1, dimensions.Item2, picture);
-        }
-
-
-
-        /// <summary>
-        /// parses the first two lines of an input file in .isad format
-        /// </summary>
-        /// <param name="allLines">the input file as an array of lines</param>
-        /// <returns>the width and height of the left canvas</returns>
-        private (int, int) ParseISADHeader(String[] allLines)
-        {
-            int width;
-            int height;
-            if (!(allLines.Length > 1) || !Regex.Match(allLines[1], @"^\d+x?\d+$", RegexOptions.None).Success)
-            {
-                throw new FileImporterException("invalid or missing canvas size definition", "format: [width]x[heigth]", 2);
-            }
-            String[] size = allLines[1].Split('x');
-            width = Convert.ToInt32(size[0]);
-            height = Convert.ToInt32(size[1]);
-            return (width, height);
-        }
-
-        /// <summary>
-        /// parses all line entries of an input file in .isad format
-        /// </summary>
-        /// <param name="allLines">the input file as an array of lines</param>
-        /// <returns>the parsed picture as a list of lines</returns>
-        private List<Line> ParseISADBody(String[] allLines, int width, int height)
-        {
-
-            String lineStartString = "line";
-            String lineEndString = "endline";
-
-            List<Line> drawing = new List<Line>();
-
-            //number of the line currently being parsed, enumeration starting at 0, body starts at the third line, therefore lin number 2
-            int i = 2;
-            //parse 'line' token and complete line definition
-            int lineStartPointer = i;
-            //holds the line number of the next expected beginning of a line definition, or of the enddrawing token
-            while (lineStartString.Equals(allLines[i]))
-            {
-                //start parsing next line
-                i++;
-                List<Point> newLine = new List<Point>();
-                while (!lineEndString.Equals(allLines[i]))
-                {
-                    if (i == allLines.Length)
-                    {
-                        throw new FileImporterException("unterminated line definition", null, (i + 1));
-                    }
-                    //parse single point definition
-                    if (!Regex.Match(allLines[i], @"^\d+;\d+$", RegexOptions.None).Success)
-                    {
-                        throw new FileImporterException("invalid Point definition: wrong format", "format: [xCoordinate];[yCoordinate]", (i + 1) );
-                    }
-                    String[] coordinates = allLines[i].Split(';');
-                    //no errors possible, convertability to int already checked above
-                    int xCoordinate = Convert.ToInt32(coordinates[0]);
-                    int yCoordinate = Convert.ToInt32(coordinates[1]);
-                    if (xCoordinate < 0 || yCoordinate < 0 || xCoordinate > width - 1 || yCoordinate > height - 1)
-                    {
-                        throw new FileImporterException("invalid Point definition: point out of bounds", null, (i + 1) );
-                    }
-                    newLine.Add(new Point(xCoordinate, yCoordinate));
-                    //start parsing next line
-                    i++;
-                }
-                //"parse" 'endline' token, syntax already checked at the beginning,  and start parsing next line
-                i++;
-                //add line to drawing
-                drawing.Add(new Line(newLine));
-                //update lineStartPointer to the presumable start of the next line
-                lineStartPointer = i;
-            }
-            //check if end of body is reached after there are no more line definitions
-            if(i != allLines.Length - 1)
-            {
-                throw new FileImporterException("missing or invalid line definition token", "line definitions start with the 'line' token", (i + 1));
-            }
-            //return parsed picture
-            return drawing;
-        }
-
-        /// <summary>
-        /// connection point for testing use only: calls ParseISADInput(String[] allLines) and directly passes the given argument (effectively bypassing the File Input functionality)
-        /// </summary>
-        /// <param name="allLines">an array holding all lines of the input file</param>
-        /// <returns>the width and height of the left canvas and the parsed picture as a list of lines</returns>
-        public (int, int, List<Line>) ParseISADInputForTesting(String[] allLines)
-        {
-            return ParseISADInput(allLines);
-        }
-
-    }
-}

+ 0 - 299
SketchAssistant/SketchAssistant/Form1.Designer.cs

@@ -1,299 +0,0 @@
-namespace SketchAssistant
-{
-    partial class Form1
-    {
-        /// <summary>
-        /// Required designer variable.
-        /// </summary>
-        private System.ComponentModel.IContainer components = null;
-
-        /// <summary>
-        /// Clean up any resources being used.
-        /// </summary>
-        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
-        protected override void Dispose(bool disposing)
-        {
-            if (disposing && (components != null))
-            {
-                components.Dispose();
-            }
-            base.Dispose(disposing);
-        }
-
-        #region Windows Form Designer generated code
-
-        /// <summary>
-        /// Required method for Designer support - do not modify
-        /// the contents of this method with the code editor.
-        /// </summary>
-        private void InitializeComponent()
-        {
-            this.components = new System.ComponentModel.Container();
-            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
-            this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
-            this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
-            this.pictureBoxRight = new System.Windows.Forms.PictureBox();
-            this.pictureBoxLeft = new System.Windows.Forms.PictureBox();
-            this.menuStrip1 = new System.Windows.Forms.MenuStrip();
-            this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
-            this.loadToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
-            this.importToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
-            this.examplePictureToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
-            this.toolStrip1 = new System.Windows.Forms.ToolStrip();
-            this.canvasButton = new System.Windows.Forms.ToolStripButton();
-            this.drawButton = new System.Windows.Forms.ToolStripButton();
-            this.deleteButton = new System.Windows.Forms.ToolStripButton();
-            this.statusStrip1 = new System.Windows.Forms.StatusStrip();
-            this.toolStripLoadStatus = new System.Windows.Forms.ToolStripStatusLabel();
-            this.backgroundWorker2 = new System.ComponentModel.BackgroundWorker();
-            this.mouseTimer = new System.Windows.Forms.Timer(this.components);
-            this.lastActionTakenLabel = new System.Windows.Forms.ToolStripStatusLabel();
-            this.undoButton = new System.Windows.Forms.ToolStripButton();
-            this.redoButton = new System.Windows.Forms.ToolStripButton();
-            this.tableLayoutPanel1.SuspendLayout();
-            ((System.ComponentModel.ISupportInitialize)(this.pictureBoxRight)).BeginInit();
-            ((System.ComponentModel.ISupportInitialize)(this.pictureBoxLeft)).BeginInit();
-            this.menuStrip1.SuspendLayout();
-            this.toolStrip1.SuspendLayout();
-            this.statusStrip1.SuspendLayout();
-            this.SuspendLayout();
-            // 
-            // tableLayoutPanel1
-            // 
-            this.tableLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
-            | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-            this.tableLayoutPanel1.BackColor = System.Drawing.SystemColors.InactiveCaption;
-            this.tableLayoutPanel1.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.OutsetDouble;
-            this.tableLayoutPanel1.ColumnCount = 2;
-            this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
-            this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
-            this.tableLayoutPanel1.Controls.Add(this.pictureBoxRight, 1, 0);
-            this.tableLayoutPanel1.Controls.Add(this.pictureBoxLeft, 0, 0);
-            this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 52);
-            this.tableLayoutPanel1.Name = "tableLayoutPanel1";
-            this.tableLayoutPanel1.RowCount = 1;
-            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
-            this.tableLayoutPanel1.Size = new System.Drawing.Size(696, 440);
-            this.tableLayoutPanel1.TabIndex = 1;
-            // 
-            // pictureBoxRight
-            // 
-            this.pictureBoxRight.Dock = System.Windows.Forms.DockStyle.Fill;
-            this.pictureBoxRight.Location = new System.Drawing.Point(349, 3);
-            this.pictureBoxRight.Margin = new System.Windows.Forms.Padding(0);
-            this.pictureBoxRight.Name = "pictureBoxRight";
-            this.pictureBoxRight.Size = new System.Drawing.Size(344, 434);
-            this.pictureBoxRight.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
-            this.pictureBoxRight.TabIndex = 6;
-            this.pictureBoxRight.TabStop = false;
-            this.pictureBoxRight.MouseDown += new System.Windows.Forms.MouseEventHandler(this.pictureBoxRight_MouseDown);
-            this.pictureBoxRight.MouseMove += new System.Windows.Forms.MouseEventHandler(this.pictureBoxRight_MouseMove);
-            this.pictureBoxRight.MouseUp += new System.Windows.Forms.MouseEventHandler(this.pictureBoxRight_MouseUp);
-            // 
-            // pictureBoxLeft
-            // 
-            this.pictureBoxLeft.BackColor = System.Drawing.SystemColors.InactiveCaption;
-            this.pictureBoxLeft.Dock = System.Windows.Forms.DockStyle.Fill;
-            this.pictureBoxLeft.Location = new System.Drawing.Point(3, 3);
-            this.pictureBoxLeft.Margin = new System.Windows.Forms.Padding(0);
-            this.pictureBoxLeft.Name = "pictureBoxLeft";
-            this.pictureBoxLeft.Size = new System.Drawing.Size(343, 434);
-            this.pictureBoxLeft.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
-            this.pictureBoxLeft.TabIndex = 5;
-            this.pictureBoxLeft.TabStop = false;
-            // 
-            // menuStrip1
-            // 
-            this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
-            this.fileToolStripMenuItem});
-            this.menuStrip1.Location = new System.Drawing.Point(0, 0);
-            this.menuStrip1.MaximumSize = new System.Drawing.Size(1000, 0);
-            this.menuStrip1.Name = "menuStrip1";
-            this.menuStrip1.Size = new System.Drawing.Size(696, 24);
-            this.menuStrip1.TabIndex = 2;
-            this.menuStrip1.Text = "menuStrip1";
-            // 
-            // fileToolStripMenuItem
-            // 
-            this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
-            this.loadToolStripMenuItem,
-            this.importToolStripMenuItem});
-            this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
-            this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20);
-            this.fileToolStripMenuItem.Text = "File";
-            // 
-            // loadToolStripMenuItem
-            // 
-            this.loadToolStripMenuItem.Name = "loadToolStripMenuItem";
-            this.loadToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
-            this.loadToolStripMenuItem.Text = "Load...";
-            this.loadToolStripMenuItem.Click += new System.EventHandler(this.loadToolStripMenuItem_Click);
-            // 
-            // importToolStripMenuItem
-            // 
-            this.importToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
-            this.examplePictureToolStripMenuItem});
-            this.importToolStripMenuItem.Name = "importToolStripMenuItem";
-            this.importToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
-            this.importToolStripMenuItem.Text = "Import...";
-            // 
-            // examplePictureToolStripMenuItem
-            // 
-            this.examplePictureToolStripMenuItem.Name = "examplePictureToolStripMenuItem";
-            this.examplePictureToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
-            this.examplePictureToolStripMenuItem.Text = "Example picture";
-            this.examplePictureToolStripMenuItem.Click += new System.EventHandler(this.examplePictureToolStripMenuItem_Click);
-            // 
-            // toolStrip1
-            // 
-            this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
-            this.canvasButton,
-            this.drawButton,
-            this.deleteButton,
-            this.undoButton,
-            this.redoButton});
-            this.toolStrip1.Location = new System.Drawing.Point(0, 24);
-            this.toolStrip1.Name = "toolStrip1";
-            this.toolStrip1.Size = new System.Drawing.Size(696, 25);
-            this.toolStrip1.TabIndex = 3;
-            this.toolStrip1.Text = "toolStrip1";
-            // 
-            // canvasButton
-            // 
-            this.canvasButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
-            this.canvasButton.Image = ((System.Drawing.Image)(resources.GetObject("canvasButton.Image")));
-            this.canvasButton.ImageTransparentColor = System.Drawing.Color.Magenta;
-            this.canvasButton.Name = "canvasButton";
-            this.canvasButton.Size = new System.Drawing.Size(76, 22);
-            this.canvasButton.Text = "New Canvas";
-            this.canvasButton.Click += new System.EventHandler(this.canvasButton_Click);
-            // 
-            // drawButton
-            // 
-            this.drawButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
-            this.drawButton.Image = ((System.Drawing.Image)(resources.GetObject("drawButton.Image")));
-            this.drawButton.ImageTransparentColor = System.Drawing.Color.Magenta;
-            this.drawButton.Name = "drawButton";
-            this.drawButton.Size = new System.Drawing.Size(38, 22);
-            this.drawButton.Text = "Draw";
-            this.drawButton.Click += new System.EventHandler(this.drawButton_Click);
-            // 
-            // deleteButton
-            // 
-            this.deleteButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
-            this.deleteButton.Image = ((System.Drawing.Image)(resources.GetObject("deleteButton.Image")));
-            this.deleteButton.ImageTransparentColor = System.Drawing.Color.Magenta;
-            this.deleteButton.Name = "deleteButton";
-            this.deleteButton.Size = new System.Drawing.Size(44, 22);
-            this.deleteButton.Text = "Delete";
-            this.deleteButton.Click += new System.EventHandler(this.deleteButton_Click);
-            // 
-            // statusStrip1
-            // 
-            this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
-            this.toolStripLoadStatus,
-            this.lastActionTakenLabel});
-            this.statusStrip1.Location = new System.Drawing.Point(0, 491);
-            this.statusStrip1.Name = "statusStrip1";
-            this.statusStrip1.Size = new System.Drawing.Size(696, 24);
-            this.statusStrip1.TabIndex = 4;
-            this.statusStrip1.Text = "statusStrip1";
-            // 
-            // toolStripLoadStatus
-            // 
-            this.toolStripLoadStatus.Name = "toolStripLoadStatus";
-            this.toolStripLoadStatus.Size = new System.Drawing.Size(40, 19);
-            this.toolStripLoadStatus.Text = "no file";
-            // 
-            // mouseTimer
-            // 
-            this.mouseTimer.Interval = 1;
-            this.mouseTimer.Tick += new System.EventHandler(this.mouseTimer_Tick);
-            // 
-            // lastActionTakenLabel
-            // 
-            this.lastActionTakenLabel.BorderSides = System.Windows.Forms.ToolStripStatusLabelBorderSides.Left;
-            this.lastActionTakenLabel.Name = "lastActionTakenLabel";
-            this.lastActionTakenLabel.Size = new System.Drawing.Size(38, 19);
-            this.lastActionTakenLabel.Text = "none";
-            this.lastActionTakenLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
-            // 
-            // undoButton
-            // 
-            this.undoButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
-            this.undoButton.Image = ((System.Drawing.Image)(resources.GetObject("undoButton.Image")));
-            this.undoButton.ImageTransparentColor = System.Drawing.Color.Magenta;
-            this.undoButton.Name = "undoButton";
-            this.undoButton.Size = new System.Drawing.Size(40, 22);
-            this.undoButton.Text = "Undo";
-            this.undoButton.Click += new System.EventHandler(this.undoButton_Click);
-            // 
-            // redoButton
-            // 
-            this.redoButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
-            this.redoButton.Image = ((System.Drawing.Image)(resources.GetObject("redoButton.Image")));
-            this.redoButton.ImageTransparentColor = System.Drawing.Color.Magenta;
-            this.redoButton.Name = "redoButton";
-            this.redoButton.Size = new System.Drawing.Size(38, 22);
-            this.redoButton.Text = "Redo";
-            this.redoButton.Click += new System.EventHandler(this.redoButton_Click);
-            // 
-            // Form1
-            // 
-            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
-            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-            this.BackColor = System.Drawing.SystemColors.MenuBar;
-            this.ClientSize = new System.Drawing.Size(696, 515);
-            this.Controls.Add(this.statusStrip1);
-            this.Controls.Add(this.toolStrip1);
-            this.Controls.Add(this.tableLayoutPanel1);
-            this.Controls.Add(this.menuStrip1);
-            this.KeyPreview = true;
-            this.MainMenuStrip = this.menuStrip1;
-            this.Margin = new System.Windows.Forms.Padding(2);
-            this.Name = "Form1";
-            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
-            this.Text = "Sketch Assistant";
-            this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
-            this.Load += new System.EventHandler(this.Form1_Load);
-            this.SizeChanged += new System.EventHandler(this.Form1_Resize);
-            this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);
-            this.tableLayoutPanel1.ResumeLayout(false);
-            ((System.ComponentModel.ISupportInitialize)(this.pictureBoxRight)).EndInit();
-            ((System.ComponentModel.ISupportInitialize)(this.pictureBoxLeft)).EndInit();
-            this.menuStrip1.ResumeLayout(false);
-            this.menuStrip1.PerformLayout();
-            this.toolStrip1.ResumeLayout(false);
-            this.toolStrip1.PerformLayout();
-            this.statusStrip1.ResumeLayout(false);
-            this.statusStrip1.PerformLayout();
-            this.ResumeLayout(false);
-            this.PerformLayout();
-
-        }
-
-        #endregion
-        private System.ComponentModel.BackgroundWorker backgroundWorker1;
-        private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
-        private System.Windows.Forms.MenuStrip menuStrip1;
-        private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
-        private System.Windows.Forms.ToolStripMenuItem loadToolStripMenuItem;
-        private System.Windows.Forms.ToolStrip toolStrip1;
-        private System.Windows.Forms.StatusStrip statusStrip1;
-        private System.Windows.Forms.ToolStripStatusLabel toolStripLoadStatus;
-        private System.ComponentModel.BackgroundWorker backgroundWorker2;
-        private System.Windows.Forms.PictureBox pictureBoxRight;
-        private System.Windows.Forms.PictureBox pictureBoxLeft;
-        private System.Windows.Forms.Timer mouseTimer;
-        private System.Windows.Forms.ToolStripButton canvasButton;
-        private System.Windows.Forms.ToolStripButton drawButton;
-        private System.Windows.Forms.ToolStripButton deleteButton;
-        private System.Windows.Forms.ToolStripMenuItem importToolStripMenuItem;
-        private System.Windows.Forms.ToolStripMenuItem examplePictureToolStripMenuItem;
-        private System.Windows.Forms.ToolStripStatusLabel lastActionTakenLabel;
-        private System.Windows.Forms.ToolStripButton undoButton;
-        private System.Windows.Forms.ToolStripButton redoButton;
-    }
-}
-

+ 0 - 802
SketchAssistant/SketchAssistant/Form1.cs

@@ -1,802 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Data;
-using System.Drawing;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-using System.Text.RegularExpressions;
-
-
-// This is the code for your desktop app.
-// Press Ctrl+F5 (or go to Debug > Start Without Debugging) to run your app.
-
-namespace SketchAssistant
-{
-    public partial class Form1 : Form
-    {
-        public Form1()
-        {
-            InitializeComponent();
-            fileImporter = new FileImporter(this);
-        }
-
-        /**********************************/
-        /*** CLASS VARIABLES START HERE ***/
-        /**********************************/
-
-        //important: add new variables only at the end of the list to keep the order of definition consistent with the order in which they are returned by GetAllVariables()
-
-        /// <summary>
-        /// Different Program States
-        /// </summary>
-        public enum ProgramState
-        {
-            Idle,
-            Draw,
-            Delete
-        }
-        /// <summary>
-        /// Current Program State
-        /// </summary>
-        private ProgramState currentState;
-        /// <summary>
-        /// instance of FileImporter to handle drawing imports
-        /// </summary>
-        private FileImporter fileImporter;
-        /// <summary>
-        /// Dialog to select a file.
-        /// </summary>
-        OpenFileDialog openFileDialog = new OpenFileDialog();
-        /// <summary>
-        /// Image loaded on the left
-        /// </summary>
-        private Image leftImage = null;
-        /// <summary>
-        /// the graphic shown in the left window, represented as a list of polylines
-        /// </summary>
-        private List<Line> leftLineList;
-        /// <summary>
-        /// Image on the right
-        /// </summary>
-        Image rightImage = null;
-        /// <summary>
-        /// Current Line being Drawn
-        /// </summary>
-        List<Point> currentLine;
-        /// <summary>
-        /// All Lines in the current session
-        /// </summary>
-        List<Tuple<bool,Line>> rightLineList = new List<Tuple<bool, Line>>();
-        /// <summary>
-        /// Whether the Mouse is currently pressed in the rightPictureBox
-        /// </summary>
-        bool mousePressed = false;
-        /// <summary>
-        /// The Position of the Cursor in the right picture box
-        /// </summary>
-        Point currentCursorPosition;
-        /// <summary>
-        /// The Previous Cursor Position in the right picture box
-        /// </summary>
-        Point previousCursorPosition;
-        /// <summary>
-        /// Queue for the cursorPositions
-        /// </summary>
-        Queue<Point> cursorPositions = new Queue<Point>();
-        /// <summary>
-        /// The graphic representation of the right image
-        /// </summary>
-        Graphics rightGraph = null;
-        /// <summary>
-        /// Lookup Matrix for checking postions of lines in the image
-        /// </summary>
-        bool[,] isFilledMatrix;
-        /// <summary>
-        /// Lookup Matrix for getting line ids at a certain postions of the image
-        /// </summary>
-        HashSet<int>[,] linesMatrix;
-        /// <summary>
-        /// Size of deletion area
-        /// </summary>
-        int deletionRadius = 2;
-        /// <summary>
-        /// History of Actions
-        /// </summary>
-        ActionHistory historyOfActions;
-        /// <summary>
-        /// List of items which will be overlayed over the right canvas.
-        /// </summary>
-        List<HashSet<Point>> overlayItems;
-        /// <summary>
-        /// The assistant responsible for the redraw mode
-        /// </summary>
-        RedrawAssistant redrawAss;
-        /// <summary>
-        /// Size of areas marking endpoints of lines in the redraw mode.
-        /// </summary>
-        int markerRadius = 10;
-
-        /******************************************/
-        /*** FORM SPECIFIC FUNCTIONS START HERE ***/
-        /******************************************/
-
-        private void Form1_Load(object sender, EventArgs e)
-        {
-            currentState = ProgramState.Idle;
-            this.DoubleBuffered = true;
-            historyOfActions = new ActionHistory(null);
-            redrawAss = new RedrawAssistant();
-            UpdateButtonStatus();
-        }
-
-        /// <summary>
-        /// Resize Function connected to the form resize event, will refresh the form when it is resized
-        /// </summary>
-        private void Form1_Resize(object sender, System.EventArgs e)
-        {
-            this.Refresh();
-            UpdateSizes();
-        }
-        
-        //Load button, will open an OpenFileDialog
-        private void loadToolStripMenuItem_Click(object sender, EventArgs e)
-        {
-            openFileDialog.Filter = "Image|*.jpg;*.png;*.jpeg";
-            if(openFileDialog.ShowDialog() == DialogResult.OK)
-            {
-                toolStripLoadStatus.Text = openFileDialog.SafeFileName;
-                leftImage = Image.FromFile(openFileDialog.FileName);
-                pictureBoxLeft.Image = leftImage;
-                //Refresh the left image box when the content is changed
-                this.Refresh();
-            }
-            UpdateButtonStatus();
-        }
-
-        /// <summary>
-        /// Import button, will open an OpenFileDialog
-        /// </summary>
-        private void examplePictureToolStripMenuItem_Click(object sender, EventArgs e)
-        {
-            if (CheckSavedStatus())
-            {
-                openFileDialog.Filter = "Interactive Sketch-Assistant Drawing|*.isad";
-                if (openFileDialog.ShowDialog() == DialogResult.OK)
-                {
-                    toolStripLoadStatus.Text = openFileDialog.SafeFileName;
-                    try
-                    {
-                        (int, int, List<Line>) values = fileImporter.ParseISADInputFile(openFileDialog.FileName);
-                        DrawEmptyCanvasLeft(values.Item1, values.Item2);
-                        BindAndDrawLeftImage(values.Item3);
-
-                        //Match The right canvas to the left
-                        historyOfActions = new ActionHistory(lastActionTakenLabel);
-                        DrawEmptyCanvasRight();
-                        isFilledMatrix = new bool[rightImage.Width, rightImage.Height];
-                        linesMatrix = new HashSet<int>[rightImage.Width, rightImage.Height];
-                        rightLineList = new List<Tuple<bool, Line>>();
-                        //Start the redraw mode
-                        redrawAss = new RedrawAssistant(leftLineList);
-                        UpdateSizes();
-                        overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, -1, false);
-                        RedrawRightImage();
-                        this.Refresh();
-                    }
-                    catch (FileImporterException ex)
-                    {
-                        ShowInfoMessage(ex.ToString());
-                    }
-                }
-            }
-            UpdateButtonStatus();
-        }
-
-        /// <summary>
-        /// Changes the state of the program to drawing
-        /// </summary>
-        private void drawButton_Click(object sender, EventArgs e)
-        {
-            if(rightImage != null)
-            {
-                if (currentState.Equals(ProgramState.Draw))
-                {
-                    ChangeState(ProgramState.Idle);
-                }
-                else
-                {
-                    ChangeState(ProgramState.Draw);
-                }
-            }
-            UpdateButtonStatus();
-        }
-
-        /// <summary>
-        /// Changes the state of the program to deletion
-        /// </summary>
-        private void deleteButton_Click(object sender, EventArgs e)
-        {
-            if (rightImage != null)
-            {
-                if (currentState.Equals(ProgramState.Delete))
-                {
-                    ChangeState(ProgramState.Idle);
-                }
-                else
-                {
-                    ChangeState(ProgramState.Delete);
-                }
-            }
-            UpdateButtonStatus();
-        }
-
-        /// <summary>
-        /// Undo an Action.
-        /// </summary>
-        private void undoButton_Click(object sender, EventArgs e)
-        {
-            if (historyOfActions.CanUndo())
-            {
-                HashSet<int> affectedLines = historyOfActions.GetCurrentAction().GetLineIDs();
-                SketchAction.ActionType  undoAction = historyOfActions.GetCurrentAction().GetActionType();
-                switch (undoAction)
-                {
-                    case SketchAction.ActionType.Delete:
-                        //Deleted Lines need to be shown
-                        ChangeLines(affectedLines, true);
-                        break;
-                    case SketchAction.ActionType.Draw:
-                        //Drawn lines need to be hidden
-                        ChangeLines(affectedLines, false);
-                        break;
-                    default:
-                        break;
-                }
-                overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, -1, false);
-                RedrawRightImage();
-            }
-            historyOfActions.MoveAction(true);
-            UpdateButtonStatus();
-        }
-
-        /// <summary>
-        /// Redo an Action.
-        /// </summary>
-        private void redoButton_Click(object sender, EventArgs e)
-        {
-            if (historyOfActions.CanRedo())
-            {
-                historyOfActions.MoveAction(false);
-                HashSet<int> affectedLines = historyOfActions.GetCurrentAction().GetLineIDs();
-                SketchAction.ActionType redoAction = historyOfActions.GetCurrentAction().GetActionType();
-                switch (redoAction)
-                {
-                    case SketchAction.ActionType.Delete:
-                        //Deleted Lines need to be redeleted
-                        ChangeLines(affectedLines, false);
-                        break;
-                    case SketchAction.ActionType.Draw:
-                        //Drawn lines need to be redrawn
-                        ChangeLines(affectedLines, true);
-                        break;
-                    default:
-                        break;
-                }
-                overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, -1, false);
-                RedrawRightImage();
-            }
-            UpdateButtonStatus();
-        }
-
-        /// <summary>
-        /// Detect Keyboard Shortcuts.
-        /// </summary>
-        private void Form1_KeyDown(object sender, KeyEventArgs e)
-        {
-            if (e.Modifiers == Keys.Control && e.KeyCode == Keys.Z)
-            {
-                undoButton_Click(sender, e);
-            }
-            if (e.Modifiers == Keys.Control && e.KeyCode == Keys.Y)
-            {
-                redoButton_Click(sender, e);
-            }
-        }
-
-        /// <summary>
-        /// Get current Mouse positon within the right picture box.
-        /// </summary>
-        private void pictureBoxRight_MouseMove(object sender, MouseEventArgs e)
-        {
-            currentCursorPosition = ConvertCoordinates(new Point(e.X, e.Y));
-        }
-        
-        /// <summary>
-        /// Hold left mouse button to start drawing.
-        /// </summary>
-        private void pictureBoxRight_MouseDown(object sender, MouseEventArgs e)
-        {
-            mousePressed = true;
-            if (currentState.Equals(ProgramState.Draw))
-            {
-                currentLine = new List<Point>();
-            }
-        }
-        
-        /// <summary>
-        /// Lift left mouse button to stop drawing and add a new Line.
-        /// </summary>
-        private void pictureBoxRight_MouseUp(object sender, MouseEventArgs e)
-        {
-            mousePressed = false;
-            if (currentState.Equals(ProgramState.Draw) && currentLine.Count > 0)
-            {
-                Line newLine = new Line(currentLine, rightLineList.Count);
-                rightLineList.Add(new Tuple<bool, Line>(true, newLine));
-                newLine.PopulateMatrixes(isFilledMatrix, linesMatrix);
-                historyOfActions.AddNewAction(new SketchAction(SketchAction.ActionType.Draw, newLine.GetID()));
-                //Execute a RedrawAssistant tick with the currently finished Line
-                overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, newLine.GetID(), true);
-                RedrawRightImage();
-            }
-            UpdateButtonStatus();
-        }
-        
-        /// <summary>
-        /// Button to create a new Canvas. Will create an empty image 
-        /// which is the size of the left image, if there is one.
-        /// If there is no image loaded the canvas will be the size of the right picture box
-        /// </summary>
-        private void canvasButton_Click(object sender, EventArgs e)
-        {
-            if (CheckSavedStatus())
-            {
-                historyOfActions = new ActionHistory(lastActionTakenLabel);
-                DrawEmptyCanvasRight();
-                //The following lines cannot be in DrawEmptyCanvas()
-                isFilledMatrix = new bool[rightImage.Width, rightImage.Height];
-                linesMatrix = new HashSet<int>[rightImage.Width, rightImage.Height];
-                rightLineList = new List<Tuple<bool, Line>>();
-                //Reinitialise the Redraw Assistant.
-                if(leftLineList != null)
-                {
-                    redrawAss = new RedrawAssistant(leftLineList);
-                    UpdateSizes();
-                    overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, -1, false);
-                    RedrawRightImage();
-                }
-            }
-            UpdateButtonStatus();
-            UpdateSizes();
-        }
-
-        /// <summary>
-        /// Add a Point on every tick to the Drawpath.
-        /// Or detect lines for deletion on every tick
-        /// </summary>
-        private void mouseTimer_Tick(object sender, EventArgs e)
-        {
-            if(cursorPositions.Count > 0) { previousCursorPosition = cursorPositions.Dequeue(); }
-            else { previousCursorPosition = currentCursorPosition; }
-            cursorPositions.Enqueue(currentCursorPosition);
-            //Drawing
-            if (currentState.Equals(ProgramState.Draw) && mousePressed)
-            {
-                rightGraph = Graphics.FromImage(rightImage);
-                currentLine.Add(currentCursorPosition);
-                Line drawline = new Line(currentLine);
-                drawline.DrawLine(rightGraph);
-                pictureBoxRight.Image = rightImage;
-                //Redraw overlay gets ticked
-                overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, rightLineList.Count, false);
-                RedrawRightImage();
-            }
-            //Deleting
-            if (currentState.Equals(ProgramState.Delete) && mousePressed)
-            {
-                List<Point> uncheckedPoints = GeometryCalculator.BresenhamLineAlgorithm(previousCursorPosition, currentCursorPosition);
-                foreach (Point currPoint in uncheckedPoints)
-                {
-                    HashSet<int> linesToDelete = CheckDeletionMatrixesAroundPoint(currPoint, deletionRadius);
-                    if (linesToDelete.Count > 0)
-                    {
-                        historyOfActions.AddNewAction(new SketchAction(SketchAction.ActionType.Delete, linesToDelete));
-                        foreach (int lineID in linesToDelete)
-                        {
-                            rightLineList[lineID] = new Tuple<bool, Line>(false, rightLineList[lineID].Item2);
-                        }
-                        RepopulateDeletionMatrixes();
-                        //Redraw overlay gets ticked
-                        overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, -1, false);
-                        RedrawRightImage();
-                    }
-                }
-            }
-        }
-
-        /***********************************/
-        /*** HELPER FUNCTIONS START HERE ***/
-        /***********************************/
-
-        /// <summary>
-        /// A function that returns a white canvas for a given width and height.
-        /// </summary>
-        /// <param name="width">The width of the canvas in pixels</param>
-        /// <param name="height">The height of the canvas in pixels</param>
-        /// <returns>The new canvas</returns>
-        private Image GetEmptyCanvas(int width, int height)
-        {
-            Image image;
-            try
-            {
-                image = new Bitmap(width, height);
-            }
-            catch(ArgumentException e)
-            {
-                ShowInfoMessage("The requested canvas size caused an error: \n" + e.ToString() + "\n The Canvas will be set to match your window.");
-                image = new Bitmap(pictureBoxLeft.Width, pictureBoxLeft.Height);
-            }
-            Graphics graph = Graphics.FromImage(image);
-            graph.FillRectangle(Brushes.White, 0, 0, width + 10, height + 10);
-            return image;
-        }
-
-        /// <summary>
-        /// Creates an empty Canvas
-        /// </summary>
-        private void DrawEmptyCanvasRight()
-        {
-            if (leftImage == null)
-            {
-                SetAndRefreshRightImage(GetEmptyCanvas(pictureBoxRight.Width, pictureBoxRight.Height));
-            }
-            else
-            {
-                SetAndRefreshRightImage(GetEmptyCanvas(leftImage.Width, leftImage.Height));
-            }
-        }
-
-        /// <summary>
-        /// Creates an empty Canvas on the left
-        /// </summary>
-        /// <param name="width"> width of the new canvas in pixels </param>
-        /// <param name="height"> height of the new canvas in pixels </param>
-        private void DrawEmptyCanvasLeft(int width, int height)
-        {
-            if (width == 0)
-            {
-                SetAndRefreshLeftImage(GetEmptyCanvas(pictureBoxLeft.Width, pictureBoxLeft.Height));
-            }
-            else
-            {
-                SetAndRefreshLeftImage(GetEmptyCanvas(width, height));
-            }
-        }
-
-        /// <summary>
-        /// Redraws all lines in lineList, for which their associated boolean value equals true.
-        /// </summary>
-        private void RedrawRightImage()
-        {
-            var workingCanvas = GetEmptyCanvas(rightImage.Width, rightImage.Height);
-            var workingGraph = Graphics.FromImage(workingCanvas);
-            //Lines
-            foreach (Tuple<bool, Line> lineBoolTuple in rightLineList)
-            {
-                if (lineBoolTuple.Item1)
-                {
-                    lineBoolTuple.Item2.DrawLine(workingGraph);
-                }
-            }
-            //The Line being currently drawn
-            if(currentLine != null && currentLine.Count > 0 && currentState.Equals(ProgramState.Draw) && mousePressed)
-            {
-                var currLine = new Line(currentLine);
-                currLine.DrawLine(workingGraph);
-            }
-            //Overlay Items
-            foreach (HashSet<Point> item in overlayItems)
-            {
-                foreach(Point p in item)
-                {
-                    workingGraph.FillRectangle(Brushes.Green, p.X, p.Y, 1, 1);
-                }
-            }
-            SetAndRefreshRightImage(workingCanvas);
-        }
-
-        /// <summary>
-        /// A function to set rightImage and to refresh the respective PictureBox with it.
-        /// </summary>
-        /// <param name="image">The new Image</param>
-        private void SetAndRefreshRightImage(Image image)
-        {
-            rightImage = image;
-            pictureBoxRight.Image = rightImage;
-            pictureBoxRight.Refresh();
-        }
-
-        /// <summary>
-        /// A function to set leftImage and to refresh the respective PictureBox with it.
-        /// </summary>
-        /// <param name="image">The new Image</param>
-        private void SetAndRefreshLeftImage(Image image)
-        {
-            leftImage = image;
-            pictureBoxLeft.Image = leftImage;
-            pictureBoxLeft.Refresh();
-        }
-
-        /// <summary>
-        /// Change the status of whether or not the lines are shown.
-        /// </summary>
-        /// <param name="lines">The HashSet containing the affected Line IDs.</param>
-        /// <param name="shown">True if the lines should be shown, false if they should be hidden.</param>
-        private void ChangeLines(HashSet<int> lines, bool shown)
-        {
-            foreach (int lineId in lines)
-            {
-                if (lineId <= rightLineList.Count - 1 && lineId >= 0)
-                {
-                    rightLineList[lineId] = new Tuple<bool, Line>(shown, rightLineList[lineId].Item2);
-                }
-            }
-            RedrawRightImage();
-        }
-
-        /// <summary>
-        /// Updates the active status of buttons. Currently draw, delete, undo and redo button.
-        /// </summary>
-        private void UpdateButtonStatus()
-        {
-            undoButton.Enabled = historyOfActions.CanUndo();
-            redoButton.Enabled = historyOfActions.CanRedo();
-            drawButton.Enabled = (rightImage != null);
-            deleteButton.Enabled = (rightImage != null);
-        }
-
-        /// <summary>
-        /// A helper function which handles tasks associated witch changing states, 
-        /// such as checking and unchecking buttons and changing the state.
-        /// </summary>
-        /// <param name="newState">The new state of the program</param>
-        private void ChangeState(ProgramState newState)
-        {
-            switch (currentState)
-            {
-                case ProgramState.Draw:
-                    drawButton.CheckState = CheckState.Unchecked;
-                    mouseTimer.Enabled = false;
-                    break;
-                case ProgramState.Delete:
-                    deleteButton.CheckState = CheckState.Unchecked;
-                    mouseTimer.Enabled = false;
-                    break;
-                default:
-                    break;
-            }
-            switch (newState)
-            {
-                case ProgramState.Draw:
-                    drawButton.CheckState = CheckState.Checked;
-                    mouseTimer.Enabled = true;
-                    break;
-                case ProgramState.Delete:
-                    deleteButton.CheckState = CheckState.Checked;
-                    mouseTimer.Enabled = true;
-                    break;
-                default:
-                    break;
-            }
-            currentState = newState;
-            pictureBoxRight.Refresh();
-        }
-
-        /// <summary>
-        /// A function that calculates the coordinates of a point on a zoomed in image.
-        /// </summary>
-        /// <param name="">The position of the mouse cursor</param>
-        /// <returns>The real coordinates of the mouse cursor on the image</returns>
-        private Point ConvertCoordinates(Point cursorPosition)
-        {
-            Point realCoordinates = new Point(5,3);
-            if(pictureBoxRight.Image == null)
-            {
-                return cursorPosition;
-            }
-
-            int widthImage = pictureBoxRight.Image.Width;
-            int heightImage = pictureBoxRight.Image.Height;
-            int widthBox = pictureBoxRight.Width;
-            int heightBox = pictureBoxRight.Height;
-
-            float imageRatio = (float)widthImage / (float)heightImage;
-            float containerRatio = (float)widthBox / (float)heightBox;
-
-            if (imageRatio >= containerRatio)
-            {
-                //Image is wider than it is high
-                float zoomFactor = (float)widthImage / (float)widthBox;
-                float scaledHeight = heightImage / zoomFactor;
-                float filler = (heightBox - scaledHeight) / 2;
-                realCoordinates.X = (int)(cursorPosition.X * zoomFactor);
-                realCoordinates.Y = (int)((cursorPosition.Y - filler) * zoomFactor);
-            }
-            else
-            {
-                //Image is higher than it is wide
-                float zoomFactor = (float)heightImage / (float)heightBox;
-                float scaledWidth = widthImage / zoomFactor;
-                float filler = (widthBox - scaledWidth) / 2;
-                realCoordinates.X = (int)((cursorPosition.X - filler) * zoomFactor);
-                realCoordinates.Y = (int)(cursorPosition.Y * zoomFactor);
-            }
-            return realCoordinates;
-        }
-
-        /// <summary>
-        /// A function that populates the matrixes needed for deletion detection with line data.
-        /// </summary>
-        private void RepopulateDeletionMatrixes()
-        {
-            if(rightImage != null)
-            {
-                isFilledMatrix = new bool[rightImage.Width,rightImage.Height];
-                linesMatrix = new HashSet<int>[rightImage.Width, rightImage.Height];
-                foreach(Tuple<bool,Line> lineTuple in rightLineList)
-                {
-                    if (lineTuple.Item1)
-                    {
-                        lineTuple.Item2.PopulateMatrixes(isFilledMatrix, linesMatrix);
-                    }
-                }
-            }
-        }
-
-        /// <summary>
-        /// A function that checks the deletion matrixes at a certain point 
-        /// and returns all Line ids at that point and in a square around it in a certain range.
-        /// </summary>
-        /// <param name="p">The point around which to check.</param>
-        /// <param name="range">The range around the point. If range is 0, only the point is checked.</param>
-        /// <returns>A List of all lines.</returns>
-        private HashSet<int> CheckDeletionMatrixesAroundPoint(Point p, int range)
-        {
-            HashSet<int> returnSet = new HashSet<int>();
-
-            foreach(Point pnt in GeometryCalculator.FilledCircleAlgorithm(p, (int)range))
-            {
-                if(pnt.X >= 0 && pnt.Y >= 0 && pnt.X < rightImage.Width && pnt.Y < rightImage.Height)
-                {
-                    if (isFilledMatrix[pnt.X, pnt.Y])
-                    {
-                        returnSet.UnionWith(linesMatrix[pnt.X, pnt.Y]);
-                    }
-                }
-            }
-            return returnSet;
-        }
-
-        /// <summary>
-        /// binds the given picture to templatePicture and draws it
-        /// </summary>
-        /// <param name="newTemplatePicture"> the new template picture, represented as a list of polylines </param>
-        /// <returns></returns>
-        private void BindAndDrawLeftImage(List<Line> newTemplatePicture)
-        {
-            leftLineList = newTemplatePicture;
-            foreach(Line l in leftLineList)
-            {
-                l.DrawLine(Graphics.FromImage(leftImage));
-            }
-        }
-
-        /// <summary>
-        /// shows the given info message in a popup and asks the user to aknowledge it
-        /// </summary>
-        /// <param name="message">the message to show</param>
-        private void ShowInfoMessage(String message)
-        {
-            MessageBox.Show(message);
-        }
-        
-        /// <summary>
-        /// Will calculate the start and endpoints of the given line on the right canvas.
-        /// </summary>
-        /// <param name="line">The line.</param>
-        /// <param name="size">The size of the circle with which the endpoints of the line are marked.</param>
-        private Tuple<HashSet<Point>, HashSet<Point>> CalculateStartAndEnd(Line line, int size)
-        {
-            var circle0 = GeometryCalculator.FilledCircleAlgorithm(line.GetStartPoint(), size);
-            var circle1 = GeometryCalculator.FilledCircleAlgorithm(line.GetEndPoint(), size);
-            var currentLineEndings = new Tuple<HashSet<Point>, HashSet<Point>>(circle0, circle1);
-            return currentLineEndings;
-        }
-
-        /// <summary>
-        /// A helper Function that updates the markerRadius & deletionRadius, considering the size of the canvas.
-        /// </summary>
-        private void UpdateSizes()
-        {
-            if (rightImage != null)
-            {
-                int widthImage = pictureBoxRight.Image.Width;
-                int heightImage = pictureBoxRight.Image.Height;
-                int widthBox = pictureBoxRight.Width;
-                int heightBox = pictureBoxRight.Height;
-                
-                float imageRatio = (float)widthImage / (float)heightImage;
-                float containerRatio = (float)widthBox / (float)heightBox;
-                float zoomFactor = 0;
-                if (imageRatio >= containerRatio)
-                {
-                    //Image is wider than it is high
-                    zoomFactor = (float)widthImage / (float)widthBox;
-                }
-                else
-                {
-                    //Image is higher than it is wide
-                    zoomFactor = (float)heightImage / (float)heightBox;
-                }
-                markerRadius = (int)(10 * zoomFactor);
-                redrawAss.SetMarkerRadius(markerRadius);
-                deletionRadius = (int)(5 * zoomFactor);
-            }
-        }
-
-        /// <summary>
-        /// Checks if there is unsaved progess, and warns the user. Returns True if it safe to continue.
-        /// </summary>
-        /// <returns>true if there is none, or the user wishes to continue without saving.
-        /// false if there is progress, and the user doesn't wish to continue.</returns>
-        private bool CheckSavedStatus()
-        {
-            if (!historyOfActions.IsEmpty())
-            {
-                return (MessageBox.Show("You have unsaved changes, do you wish to continue?",
-                    "Attention", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes);
-            }
-            return true;
-        }
-
-        /********************************************/
-        /*** TESTING RELATED FUNCTIONS START HERE ***/
-        /********************************************/
-
-        /// <summary>
-        /// returns all instance variables in the order of their definition for testing
-        /// </summary>
-        /// <returns>A list of tuples containing names of variables and the variable themselves. 
-        /// Cast according to the Type definitions in the class variable section.</returns>
-        public List<Tuple<String, Object>>GetAllVariables()
-        {
-            var objArr = new (String, object)[] { ("currentState", currentState), ("fileImporter", fileImporter), ("openFileDialog", openFileDialog),
-                ("leftImage", leftImage), ("leftLineList", leftLineList), ("rightImage", rightImage), ("currentLine", currentLine),
-                ("rightLineList", rightLineList), ("mousePressed", mousePressed), ("currentCursorPosition", currentCursorPosition),
-                ("previousCursorPosition", previousCursorPosition), ("cursorPositions", cursorPositions), ("rightGraph", rightGraph),
-                ("isFilledMatrix", isFilledMatrix), ("linesMatrix", linesMatrix), ("deletionRadius", deletionRadius),
-                ("historyOfActions", historyOfActions), ("overlayItems", overlayItems), ("redrawAss", redrawAss), ("markerRadius", markerRadius) };
-            var varArr = new List<Tuple<String, Object>>();
-            foreach((String, object) obj in objArr)
-            {
-                varArr.Add(new Tuple<string, object>(obj.Item1, obj.Item2));
-            }
-            return varArr;
-        }
-
-        /// <summary>
-        /// public method wrapper for testing purposes, invoking DrawEmptyCanvas(...) and BindAndDrawLeftImage(...)
-        /// </summary>
-        /// <param name="width">width of the parsed image</param>
-        /// <param name="height">height of the parsed image</param>
-        /// <param name="newImage">the parsed image</param>
-        public void CreateCanvasAndSetPictureForTesting(int width, int height, List<Line> newImage)
-        {
-            DrawEmptyCanvasLeft(width, height);
-            BindAndDrawLeftImage(newImage);
-        }
-    }
-}

+ 0 - 217
SketchAssistant/SketchAssistant/Form1.resx

@@ -1,217 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
-  <!-- 
-    Microsoft ResX Schema 
-    
-    Version 2.0
-    
-    The primary goals of this format is to allow a simple XML format 
-    that is mostly human readable. The generation and parsing of the 
-    various data types are done through the TypeConverter classes 
-    associated with the data types.
-    
-    Example:
-    
-    ... ado.net/XML headers & schema ...
-    <resheader name="resmimetype">text/microsoft-resx</resheader>
-    <resheader name="version">2.0</resheader>
-    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
-    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
-    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
-    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
-    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
-        <value>[base64 mime encoded serialized .NET Framework object]</value>
-    </data>
-    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
-        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
-        <comment>This is a comment</comment>
-    </data>
-                
-    There are any number of "resheader" rows that contain simple 
-    name/value pairs.
-    
-    Each data row contains a name, and value. The row also contains a 
-    type or mimetype. Type corresponds to a .NET class that support 
-    text/value conversion through the TypeConverter architecture. 
-    Classes that don't support this are serialized and stored with the 
-    mimetype set.
-    
-    The mimetype is used for serialized objects, and tells the 
-    ResXResourceReader how to depersist the object. This is currently not 
-    extensible. For a given mimetype the value must be set accordingly:
-    
-    Note - application/x-microsoft.net.object.binary.base64 is the format 
-    that the ResXResourceWriter will generate, however the reader can 
-    read any of the formats listed below.
-    
-    mimetype: application/x-microsoft.net.object.binary.base64
-    value   : The object must be serialized with 
-            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
-            : and then encoded with base64 encoding.
-    
-    mimetype: application/x-microsoft.net.object.soap.base64
-    value   : The object must be serialized with 
-            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
-            : and then encoded with base64 encoding.
-
-    mimetype: application/x-microsoft.net.object.bytearray.base64
-    value   : The object must be serialized into a byte array 
-            : using a System.ComponentModel.TypeConverter
-            : and then encoded with base64 encoding.
-    -->
-  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
-    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
-    <xsd:element name="root" msdata:IsDataSet="true">
-      <xsd:complexType>
-        <xsd:choice maxOccurs="unbounded">
-          <xsd:element name="metadata">
-            <xsd:complexType>
-              <xsd:sequence>
-                <xsd:element name="value" type="xsd:string" minOccurs="0" />
-              </xsd:sequence>
-              <xsd:attribute name="name" use="required" type="xsd:string" />
-              <xsd:attribute name="type" type="xsd:string" />
-              <xsd:attribute name="mimetype" type="xsd:string" />
-              <xsd:attribute ref="xml:space" />
-            </xsd:complexType>
-          </xsd:element>
-          <xsd:element name="assembly">
-            <xsd:complexType>
-              <xsd:attribute name="alias" type="xsd:string" />
-              <xsd:attribute name="name" type="xsd:string" />
-            </xsd:complexType>
-          </xsd:element>
-          <xsd:element name="data">
-            <xsd:complexType>
-              <xsd:sequence>
-                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
-                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
-              </xsd:sequence>
-              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
-              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
-              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
-              <xsd:attribute ref="xml:space" />
-            </xsd:complexType>
-          </xsd:element>
-          <xsd:element name="resheader">
-            <xsd:complexType>
-              <xsd:sequence>
-                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
-              </xsd:sequence>
-              <xsd:attribute name="name" type="xsd:string" use="required" />
-            </xsd:complexType>
-          </xsd:element>
-        </xsd:choice>
-      </xsd:complexType>
-    </xsd:element>
-  </xsd:schema>
-  <resheader name="resmimetype">
-    <value>text/microsoft-resx</value>
-  </resheader>
-  <resheader name="version">
-    <value>2.0</value>
-  </resheader>
-  <resheader name="reader">
-    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-  </resheader>
-  <resheader name="writer">
-    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-  </resheader>
-  <metadata name="backgroundWorker1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
-    <value>122, 17</value>
-  </metadata>
-  <metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
-    <value>286, 17</value>
-  </metadata>
-  <metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
-    <value>401, 17</value>
-  </metadata>
-  <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
-  <data name="canvasButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
-    <value>
-        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
-        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
-        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
-        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
-        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
-        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
-        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
-        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
-        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
-        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
-        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
-</value>
-  </data>
-  <data name="drawButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
-    <value>
-        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
-        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
-        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
-        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
-        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
-        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
-        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
-        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
-        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
-        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
-        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
-</value>
-  </data>
-  <data name="deleteButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
-    <value>
-        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
-        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
-        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
-        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
-        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
-        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
-        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
-        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
-        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
-        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
-        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
-</value>
-  </data>
-  <metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
-    <value>506, 17</value>
-  </metadata>
-  <metadata name="backgroundWorker2.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
-    <value>622, 17</value>
-  </metadata>
-  <metadata name="mouseTimer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
-    <value>786, 17</value>
-  </metadata>
-  <data name="undoButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
-    <value>
-        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
-        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
-        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
-        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
-        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
-        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
-        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
-        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
-        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
-        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
-        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
-</value>
-  </data>
-  <data name="redoButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
-    <value>
-        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
-        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
-        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
-        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
-        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
-        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
-        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
-        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
-        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
-        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
-        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
-</value>
-  </data>
-  <metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-    <value>47</value>
-  </metadata>
-</root>

+ 0 - 204
SketchAssistant/SketchAssistant/GeometryCalculator.cs

@@ -1,204 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Drawing;
-
-namespace SketchAssistant
-{
-    /// <summary>
-    /// A class that contains all algorithms related to geometry.
-    /// </summary>
-    public static class GeometryCalculator
-    {
-        /// <summary>
-        /// An implementation of the Bresenham Line Algorithm,
-        /// which calculates the points of a circle in a radius around a center point.
-        /// Implemented with the help of code examples on Wikipedia.
-        /// </summary>
-        /// <param name="center">The center of the circle.</param>
-        /// <param name="radius">The radius of the circle, 
-        /// when it is zero or less, only the midpoint is returned</param>
-        /// <returns>The HashSet containing all Points on the circle.</returns>
-        public static HashSet<Point> BresenhamCircleAlgorithm(Point center, int radius)
-        {
-            if(radius <= 0) { return new HashSet<Point> { center }; }
-
-            int x = radius - 1;
-            int y = 0;
-            int dx = 1;
-            int dy = 1;
-            int err = dx - (radius * 2);
-            HashSet<Point> returnSet = new HashSet<Point>();
-            
-            while (x >= y)
-            {
-                returnSet.Add(new Point(center.X + x, center.Y + y));
-                returnSet.Add(new Point(center.X + y, center.Y + x));
-                returnSet.Add(new Point(center.X - y, center.Y + x));
-                returnSet.Add(new Point(center.X - x, center.Y + y));
-                returnSet.Add(new Point(center.X - x, center.Y - y));
-                returnSet.Add(new Point(center.X - y, center.Y - x));
-                returnSet.Add(new Point(center.X + y, center.Y - x));
-                returnSet.Add(new Point(center.X + x, center.Y - y));
-
-                if (err <= 0)
-                {
-                    y++;
-                    err += dy;
-                    dy += 2;
-                }
-
-                if (err > 0)
-                {
-                    x--;
-                    dx += 2;
-                    err += dx - (radius * 2);
-                }
-            }
-            return returnSet;
-        }
-
-        /// <summary>
-        /// A simple algorithm that returns a filled circle with a radius and a center point.
-        /// </summary>
-        /// <param name="center">The center point of the alorithm </param>
-        /// <param name="radius">The radius of the circle, if its less or equal to 1 
-        /// only the center point is returned. </param>
-        /// <returns>All the points in or on the circle.</returns>
-        public static HashSet<Point> FilledCircleAlgorithm(Point center, int radius)
-        {
-            HashSet<Point> returnSet = new HashSet<Point> { center };
-            //Fill the circle
-            for (int x = 0; x < radius; x++)
-            {
-                for (int y = 0; y < radius; y++)
-                {
-                    //Check if point is on or in the circle
-                    if((x*x + y*y - radius * radius) <= 0)
-                    {
-                        returnSet.Add(new Point(center.X + x, center.Y + y));
-                        returnSet.Add(new Point(center.X - x, center.Y + y));
-                        returnSet.Add(new Point(center.X + x, center.Y - y));
-                        returnSet.Add(new Point(center.X - x, center.Y - y));
-                    }
-                }
-            }
-            return returnSet;
-        }
-
-        /// <summary>
-        /// An implementation of the Bresenham Line Algorithm, 
-        /// which calculates all points between two points in a straight line.
-        /// Implemented using the pseudocode on Wikipedia.
-        /// </summary>
-        /// <param name="p0">The start point</param>
-        /// <param name="p1">The end point</param>
-        /// <returns>All points between p0 and p1 (including p0 and p1)</returns>
-        public static List<Point> BresenhamLineAlgorithm(Point p0, Point p1)
-        {
-            int deltaX = p1.X - p0.X;
-            int deltaY = p1.Y - p0.Y;
-            List<Point> returnList;
-
-            if (Math.Abs(deltaY) < Math.Abs(deltaX))
-            {
-                if (p0.X > p1.X)
-                {
-                    returnList = GetLineLow(p1.X, p1.Y, p0.X, p0.Y);
-                    returnList.Reverse();
-                }
-                else
-                {
-                    returnList = GetLineLow(p0.X, p0.Y, p1.X, p1.Y);
-                }
-            }
-            else
-            {
-                if (p0.Y > p1.Y)
-                {
-                    returnList = GetLineHigh(p1.X, p1.Y, p0.X, p0.Y);
-                    returnList.Reverse();
-                }
-                else
-                {
-                    returnList = GetLineHigh(p0.X, p0.Y, p1.X, p1.Y);
-                }
-            }
-            return returnList;
-        }
-
-        /// <summary>
-        /// Helping function of the Bresenham Line algorithm,
-        /// under the assumption that abs(deltaY) is smaller than abs(deltX)
-        /// and x0 is smaller than x1
-        /// </summary>
-        /// <param name="x0">x value of point 0</param>
-        /// <param name="y0">y value of point 0</param>
-        /// <param name="x1">x value of point 1</param>
-        /// <param name="y1">y value of point 1</param>
-        /// <returns>All points on the line between the two points</returns>
-        private static List<Point> GetLineLow(int x0, int y0, int x1, int y1)
-        {
-            List<Point> returnList = new List<Point>();
-            int dx = x1 - x0;
-            int dy = y1 - y0;
-            int yi = 1;
-            if (dy < 0)
-            {
-                yi = -1;
-                dy = -dy;
-            }
-            int D = 2 * dy - dx;
-            int y = y0;
-            for (int x = x0; x <= x1; x++)
-            {
-                returnList.Add(new Point(x, y));
-                if (D > 0)
-                {
-                    y = y + yi;
-                    D = D - 2 * dx;
-                }
-                D = D + 2 * dy;
-            }
-            return returnList;
-        }
-
-        /// <summary>
-        /// Helping function of the Bresenham Line algorithm,
-        /// under the assumption that abs(deltaY) is larger or equal than abs(deltX)
-        /// and y0 is smaller than y1
-        /// </summary>
-        /// <param name="x0">x value of point 0</param>
-        /// <param name="y0">y value of point 0</param>
-        /// <param name="x1">x value of point 1</param>
-        /// <param name="y1">y value of point 1</param>
-        /// <returns>All points on the line between the two points</returns>
-        private static List<Point> GetLineHigh(int x0, int y0, int x1, int y1)
-        {
-            List<Point> returnList = new List<Point>();
-            int dx = x1 - x0;
-            int dy = y1 - y0;
-            int xi = 1;
-            if (dx < 0)
-            {
-                xi = -1;
-                dx = -dx;
-            }
-            int D = 2 * dx - dy;
-            int x = x0;
-            for (int y = y0; y <= y1; y++)
-            {
-                returnList.Add(new Point(x, y));
-                if (D > 0)
-                {
-                    x = x + xi;
-                    D = D - 2 * dy;
-                }
-                D = D + 2 * dx;
-            }
-            return returnList;
-        }
-    }
-}

+ 0 - 152
SketchAssistant/SketchAssistant/Line.cs

@@ -1,152 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Drawing;
-
-namespace SketchAssistant
-{
-    public class Line
-    {
-        /// <summary>
-        /// list saving all the points of the line in the order of the path from start to end point
-        /// </summary>
-        private List<Point> linePoints;
-        /// <summary>
-        /// unique identifier of this Line object
-        /// </summary>
-        private int identifier;
-        /// <summary>
-        /// flag showing if this is only a temporary line
-        /// </summary>
-        private bool isTemporary;
-
-        /// <summary>
-        /// The constructor for lines which are only temporary.
-        /// If you want nice lines use the other constructor.
-        /// </summary>
-        /// <param name="points">The points of the line</param>
-        public Line(List<Point> points)
-        {
-            linePoints = new List<Point>(points);
-            isTemporary = true;
-        }
-
-        /// <summary>
-        /// The constructor for lines, which will be more resource efficient 
-        /// and have the ability to populate deletion matrixes.
-        /// </summary>
-        /// <param name="points">The points of the line</param>
-        /// <param name="id">The identifier of the line</param>
-        public Line(List<Point> points, int id)
-        {
-            linePoints = new List<Point>(points);
-            identifier = id;
-            CleanPoints();
-            isTemporary = false;
-        }
-
-        public Point GetStartPoint()
-        {
-            return linePoints.First();
-        }
-
-        public Point GetEndPoint()
-        {
-            return linePoints.Last();
-        }
-
-        public List<Point> GetPoints()
-        {
-            return linePoints;
-        }
-
-        public int GetID()
-        {
-            return identifier;
-        }
-
-        /// <summary>
-        /// A function that takes a Graphics element and returns it with
-        /// the line drawn on it.
-        /// </summary>
-        /// <param name="canvas">The Graphics element on which the line shall be drawn</param>
-        /// <returns>The given Graphics element with the additional line</returns>
-        public Graphics DrawLine(Graphics canvas)
-        {
-            for(int i = 0; i < linePoints.Count - 1 ; i++)
-            {
-                canvas.DrawLine(Pens.Black, linePoints[i], linePoints[i + 1]);
-            }
-            //If there is only one point
-            if(linePoints.Count == 1){ canvas.FillRectangle(Brushes.Black, linePoints[0].X, linePoints[0].Y, 1, 1); }
-            return canvas;
-        }
-
-        /// <summary>
-        /// A function that will take to matrixes and populate the with the line data of this line object
-        /// </summary>
-        /// <param name="boolMatrix">The Matrix of booleans, in which is saved wether there is a line at this position.</param>
-        /// <param name="listMatrix">The Matrix of Lists of integers, in which is saved which lines are at this position</param>
-        public void PopulateMatrixes(bool[,] boolMatrix, HashSet<int>[,] listMatrix)
-        {
-            if(!isTemporary)
-            {
-                foreach (Point currPoint in linePoints)
-                {
-                    if (currPoint.X >= 0 && currPoint.Y >= 0 && 
-                        currPoint.X < boolMatrix.GetLength(0) && currPoint.Y < boolMatrix.GetLength(1))
-                    {
-                        boolMatrix[currPoint.X, currPoint.Y] = true;
-                        if (listMatrix[currPoint.X, currPoint.Y] == null)
-                        {
-                            listMatrix[currPoint.X, currPoint.Y] = new HashSet<int>();
-                        }
-                        listMatrix[currPoint.X, currPoint.Y].Add(identifier);
-                    }
-                }
-            }
-        }
-
-        /// <summary>
-        /// Removes duplicate points from the line object
-        /// </summary>
-        private void CleanPoints()
-        {
-            if (linePoints.Count > 1)
-            {
-                List<Point> newList = new List<Point>();
-                List<Point> tempList = new List<Point>();
-                //Since Point is non-nullable, we must ensure the nullPoints, 
-                //which we remove can not possibly be points of the original given line.
-                int nullValue = linePoints[0].X + 1;
-                //Fill the gaps between points
-                for (int i = 0; i < linePoints.Count - 1; i++)
-                {
-                    nullValue += linePoints[i + 1].X;
-                    List<Point> partialList = GeometryCalculator.BresenhamLineAlgorithm(linePoints[i], linePoints[i + 1]);
-                    tempList.AddRange(partialList);
-                }
-                Point nullPoint = new Point(nullValue, 0);
-                //Set duplicate points to the null point
-                for (int i = 1; i < tempList.Count; i++)
-                {
-                    if ((tempList[i].X == tempList[i - 1].X) && (tempList[i].Y == tempList[i - 1].Y))
-                    {
-                        tempList[i - 1] = nullPoint;
-                    }
-                }
-                //remove the null points
-                foreach (Point tempPoint in tempList)
-                {
-                    if (tempPoint.X != nullValue)
-                    {
-                        newList.Add(tempPoint);
-                    }
-                }
-                linePoints = new List<Point>(newList);
-            }
-        }
-    }
-}

+ 0 - 22
SketchAssistant/SketchAssistant/Program.cs

@@ -1,22 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-
-namespace SketchAssistant
-{
-    static class Program
-    {
-        /// <summary>
-        /// The main entry point for the application.
-        /// </summary>
-        [STAThread]
-        static void Main()
-        {
-            Application.EnableVisualStyles();
-            Application.SetCompatibleTextRenderingDefault(false);
-            Application.Run(new Form1());
-        }
-    }
-}

+ 0 - 36
SketchAssistant/SketchAssistant/Properties/AssemblyInfo.cs

@@ -1,36 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// 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("SketchAssistant")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("SketchAssistant")]
-[assembly: AssemblyCopyright("Copyright ©  2018")]
-[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)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("0336f628-a2f7-4170-8b2e-9277c23118d4")]
-
-// 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")]

+ 0 - 7
SketchAssistant/SketchAssistant/Properties/Settings.settings

@@ -1,7 +0,0 @@
-<?xml version='1.0' encoding='utf-8'?>
-<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
-  <Profiles>
-    <Profile Name="(Default)" />
-  </Profiles>
-  <Settings />
-</SettingsFile>

+ 0 - 221
SketchAssistant/SketchAssistant/RedrawAssistant.cs

@@ -1,221 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Collections;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Drawing;
-
-namespace SketchAssistant
-{
-    public class RedrawAssistant
-    {
-        /// <summary>
-        /// The lines of the left image, with a boolean indicating if they have been redrawn
-        /// and an integer that is the same as the line id of the respective line in the right image.
-        /// </summary>
-        List<Tuple<Line, bool, int>> linesToRedraw;
-        /// <summary>
-        /// The Start and End points of all lines in linesToRedraw in the same order.
-        /// </summary>
-        List<Tuple<HashSet<Point>, HashSet<Point>>> startAndEndPoints;
-        /// <summary>
-        /// A Hashtable for quick lookup for a line id and its respective tuple in linesToRedraw
-        /// </summary>
-        Hashtable redrawnLineLookupTable;
-        /// <summary>
-        /// The position of the line currently being redrawn in the startAndEndPoints 
-        /// & linesToRedraw lists. -1 if no line is being redrawn.
-        /// </summary>
-        int lineBeingRedrawn;
-        /// <summary>
-        /// The id of the line being drawn on the right side. -1 if no line is being drawn.
-        /// </summary>
-        int currentLineID;
-        /// <summary>
-        /// Whether or not the user is currently redrawing a line.
-        /// </summary>
-        bool currentlyRedrawing;
-        /// <summary>
-        /// Whether or not the RedrawAssistant is active.
-        /// </summary>
-        bool isActive;
-        /// <summary>
-        /// The radius of the markers for redrawing.
-        /// </summary>
-        int markerRadius = 5;
-
-        /// <summary>
-        /// The Constructor for an inactive RedrawAssistant.
-        /// </summary>
-        public RedrawAssistant()
-        {
-            isActive = false;
-        }
-
-        /// <summary>
-        /// The constructor for an active RedrawAssistant
-        /// </summary>
-        /// <param name="redrawItem">The lines that shall be redrawn</param>
-        public RedrawAssistant(List<Line> redrawItem)
-        {
-            linesToRedraw = new List<Tuple<Line, bool, int>>();
-            startAndEndPoints = new List<Tuple<HashSet<Point>, HashSet<Point>>>();
-            isActive = true;
-            currentlyRedrawing = false;
-            lineBeingRedrawn = -1;
-            redrawnLineLookupTable = new Hashtable();
-            foreach (Line line in redrawItem)
-            {
-                linesToRedraw.Add(new Tuple<Line, bool, int>(line, false, -1));
-            }
-            SetMarkerRadius(5);
-        }
-
-        /// <summary>
-        /// The main functionality of the RedrawAssistant, which updates the Assistant according to the inputs given.
-        /// </summary>
-        /// <param name="currentPoint">The current position of the cursor, as a point</param>
-        /// <param name="rightLines">The lines on the right canvas</param>
-        /// <param name="currLineID">The id of the line currently being drawn.</param>
-        /// <param name="lineFinished">A boolean to indicate that the line is finished</param>
-        /// <returns>A List of HashSets of Points, which are markers for the user to redraw lines.</returns>
-        public List<HashSet<Point>> Tick(Point currentPoint, List<Tuple<bool, Line>> rightLines, int currLineID, bool lineFinished)
-        {
-            List<HashSet<Point>> returnList = new List<HashSet<Point>>();
-            if (!isActive) { return returnList; }
-            Tuple<Line, bool, int> newLineTuple = null;
-            var returnAllStartPoints = true;
-            CheckForUndrawnLines(rightLines);
-
-            // Checking if a startpoint is intersected
-            if (!currentlyRedrawing)
-            {
-                for (int i = 0; i < linesToRedraw.Count; i++)
-                {
-                    Tuple<Line, bool, int> tup = linesToRedraw[i];
-                    if (!tup.Item2)
-                    {
-                        if (startAndEndPoints[i].Item1.Contains(currentPoint))
-                        {
-                            currentlyRedrawing = true;
-                            lineBeingRedrawn = i;
-                            currentLineID = currLineID;
-                            returnList.Add(startAndEndPoints[i].Item1);
-                            returnList.Add(startAndEndPoints[i].Item2);
-                            returnAllStartPoints = false;
-                        }
-                    }
-                }
-            }
-            //Currently redrawing a line, but a line hasn't been finished drawing.
-            else if (!lineFinished)
-            {
-                returnList.Add(startAndEndPoints[lineBeingRedrawn].Item1);
-                returnList.Add(startAndEndPoints[lineBeingRedrawn].Item2);
-                returnAllStartPoints = false;
-            }
-            //Line is finished, check if it is in the correct endpoint
-            else if (currLineID == currentLineID && startAndEndPoints[lineBeingRedrawn].Item2.Contains(currentPoint))
-            {
-                newLineTuple = new Tuple<Line, bool, int>(linesToRedraw[lineBeingRedrawn].Item1, true, currLineID);
-                currentlyRedrawing = false;
-                lineBeingRedrawn = -1;
-                currentLineID = -1;
-            }
-            //Line is finished, but not in the correct endpoint
-            else
-            {
-                currentlyRedrawing = false;
-                lineBeingRedrawn = -1;
-                currentLineID = -1;
-            }
-
-            //Replace the changed line tuple in linesToRedraw
-            if(newLineTuple != null)
-            {
-                var newLine = newLineTuple.Item1;
-                for (int i = 0; i < linesToRedraw.Count; i++)
-                {
-                    var redrawLine = linesToRedraw[i].Item1;
-                    if (redrawLine.GetID() == newLine.GetID() 
-                        && redrawLine.GetStartPoint().Equals(newLine.GetStartPoint())
-                        && redrawLine.GetEndPoint().Equals(newLine.GetEndPoint()))
-                    {
-                        redrawnLineLookupTable.Add(currLineID, i);
-                        linesToRedraw[i] = newLineTuple;
-                    }
-                }
-            }
-
-            //Add all the startpoints to the list being returned
-            if (returnAllStartPoints)
-            {
-                for (int i = 0; i < linesToRedraw.Count; i++)
-                {
-                    if (!linesToRedraw[i].Item2)
-                    {
-                        returnList.Add(startAndEndPoints[i].Item1);
-                    }
-                }
-            }
-
-            return returnList;
-        }
-
-        /// <summary>
-        /// A helping function which checks for lines where previously redrawn, but were removed from the image again.
-        /// </summary>
-        /// <param name="rightLines">The lines in the right image.</param>
-        private void CheckForUndrawnLines(List<Tuple<bool, Line>> rightLines)
-        {
-            for (int i = 0; i < rightLines.Count; i++)
-            {
-                if (redrawnLineLookupTable.ContainsKey(rightLines[i].Item2.GetID()))
-                {
-                    if (!rightLines[i].Item1)
-                    {
-                        int listPos = (int)redrawnLineLookupTable[rightLines[i].Item2.GetID()];
-                        var oldTup = linesToRedraw[listPos];
-                        linesToRedraw[listPos] = new Tuple<Line, bool, int>(oldTup.Item1, false, -1);
-                    }
-                    else
-                    {
-                        int listPos = (int)redrawnLineLookupTable[rightLines[i].Item2.GetID()];
-                        var oldTup = linesToRedraw[listPos];
-                        linesToRedraw[listPos] = new Tuple<Line, bool, int>(oldTup.Item1, true, rightLines[i].Item2.GetID());
-                    }
-                }
-            }
-        }
-
-        /// <summary>
-        /// A function to set the marker radius for the markers returned by the RedrawAssistant
-        /// </summary>
-        /// <param name="markerRad">The Radius of the markers.</param>
-        public void SetMarkerRadius(int markerRad)
-        {
-            markerRadius = markerRad;
-            if (isActive)
-            {
-                startAndEndPoints = new List<Tuple<HashSet<Point>, HashSet<Point>>>();
-                foreach (Tuple<Line, bool, int> tup in linesToRedraw)
-                {
-                    startAndEndPoints.Add(CalculateStartAndEnd(tup.Item1));
-                }
-            }
-        }
-
-        /// <summary>
-        /// Will calculate the start and endpoints of the given line.
-        /// </summary>
-        /// <param name="line">The given line.</param>
-        private Tuple<HashSet<Point>, HashSet<Point>> CalculateStartAndEnd(Line line)
-        {
-            var circle0 = GeometryCalculator.FilledCircleAlgorithm(line.GetStartPoint(), markerRadius);
-            var circle1 = GeometryCalculator.FilledCircleAlgorithm(line.GetEndPoint(), markerRadius);
-            var currentLineEndings = new Tuple<HashSet<Point>, HashSet<Point>>(circle0, circle1);
-            return currentLineEndings;
-        }
-    }
-}

+ 0 - 6
SketchAssistant/SketchAssistant/packages.config

@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
-  <package id="OpenCover" version="4.6.519" targetFramework="net461" />
-  <package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net461" />
-  <package id="System.Threading.Tasks.Extensions" version="4.5.1" targetFramework="net461" />
-</packages>

+ 24 - 17
SketchAssistant/SketchAssistant/ActionHistory.cs → SketchAssistant/SketchAssistantWPF/ActionHistory.cs

@@ -3,9 +3,8 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
-using System.Windows.Forms;
 
-namespace SketchAssistant
+namespace SketchAssistantWPF
 {
     public class ActionHistory
     {
@@ -13,22 +12,31 @@ namespace SketchAssistant
         List<SketchAction> actionHistory;
         //The current position in the actionHistory
         Tuple<int, SketchAction> currentAction;
-        //The label in which the current action is displayed
-        ToolStripStatusLabel displayLabel;
 
-        public ActionHistory(ToolStripStatusLabel displayPosition)
+        public ActionHistory()
         {
-            displayLabel = displayPosition;
             actionHistory = new List<SketchAction>();
             currentAction = new Tuple<int, SketchAction>(-1, null);
             AddNewAction(new SketchAction(SketchAction.ActionType.Start, -1));
         }
 
+        /// <summary>
+        /// Resets the action history to its initial state.
+        /// </summary>
+        /// <returns>The new Last Action taken.</returns>
+        public String Reset()
+        {
+            actionHistory.Clear();
+            currentAction = new Tuple<int, SketchAction>(-1, null);
+            return AddNewAction(new SketchAction(SketchAction.ActionType.Start, -1));
+        }
+
         /// <summary>
         /// Adds a new action to the action history.
         /// </summary>
         /// <param name="newAction">The newly added action.</param>
-        public void AddNewAction(SketchAction newAction)
+        /// <returns>The message to be displayed</returns>
+        public String AddNewAction(SketchAction newAction)
         {
             //The current Action is before the last action taken, delete everything after the current action.
             if (currentAction.Item1 < actionHistory.Count - 1)
@@ -37,24 +45,25 @@ namespace SketchAssistant
             }
             actionHistory.Add(newAction);
             currentAction = new Tuple<int, SketchAction>(actionHistory.Count - 1, newAction);
-            UpdateStatusLabel();
+            return UpdateStatusLabel();
         }
 
         /// <summary>
         /// Changes the currentAction.
         /// </summary>
         /// <param name="moveBack">If True, moves the current action back one slot, if False, moves it forward.</param>
-        public void MoveAction(bool moveBack)
+        /// <returns>The message to be displayed</returns>
+        public String MoveAction(bool moveBack)
         {
-            if(moveBack && CanUndo())
+            if (moveBack && CanUndo())
             {
                 currentAction = new Tuple<int, SketchAction>(currentAction.Item1 - 1, actionHistory[currentAction.Item1 - 1]);
             }
-            if(!moveBack && CanRedo())
+            if (!moveBack && CanRedo())
             {
                 currentAction = new Tuple<int, SketchAction>(currentAction.Item1 + 1, actionHistory[currentAction.Item1 + 1]);
             }
-            UpdateStatusLabel();
+            return UpdateStatusLabel();
         }
 
         /// <summary>
@@ -99,12 +108,10 @@ namespace SketchAssistant
         /// <summary>
         /// Updates the status label if there is one given.
         /// </summary>
-        private void UpdateStatusLabel()
+        /// <returns>The message to be displayed</returns>
+        private String UpdateStatusLabel()
         {
-            if (displayLabel != null)
-            {
-                displayLabel.Text = "Last Action: " + currentAction.Item2.GetActionInformation();
-            }
+            return "Last Action: " + currentAction.Item2.GetActionInformation();
         }
     }
 }

+ 6 - 0
SketchAssistant/SketchAssistantWPF/App.config

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+    <startup> 
+        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
+    </startup>
+</configuration>

+ 9 - 0
SketchAssistant/SketchAssistantWPF/App.xaml

@@ -0,0 +1,9 @@
+<Application x:Class="SketchAssistantWPF.App"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:local="clr-namespace:SketchAssistantWPF"
+             StartupUri="MainWindow.xaml">
+    <Application.Resources>
+         
+    </Application.Resources>
+</Application>

+ 17 - 0
SketchAssistant/SketchAssistantWPF/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 SketchAssistantWPF
+{
+    /// <summary>
+    /// Interaction logic for App.xaml
+    /// </summary>
+    public partial class App : Application
+    {
+    }
+}

Разлика између датотеке није приказан због своје велике величине
+ 15 - 0
SketchAssistant/SketchAssistantWPF/DebugData.cs


+ 1685 - 0
SketchAssistant/SketchAssistantWPF/FileImporter.cs

@@ -0,0 +1,1685 @@
+using System;
+using System.Collections.Generic;
+//using System.Drawing;
+using System.Globalization;
+using System.Windows;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+
+namespace SketchAssistantWPF
+{
+    public class FileImporter
+    {
+
+        /// <summary>
+        /// scale factor for coordinates of svg file
+        /// </summary>
+        double scale;
+
+        /// <summary>
+        /// line pointer for the current svg document
+        /// </summary>
+        int i;
+
+        /// <summary>
+        /// array containing all characters interpreted as whitespaces which seperate words/tokens in the input file
+        /// </summary>
+        readonly char[] whitespaces = new char[] { ' ' , ',' };
+
+        /// <summary>
+        /// number of points to create along the outline of an ellipse, divisible by 4
+        /// </summary>
+        readonly int samplingRateEllipse = 12;
+
+        /// <summary>
+        /// number of points to create on a bezier curve, including start and end point (even number will result in "flat" bezier curves, uneven number in "pointed" ones)
+        /// </summary>
+        readonly int samplingRateBezier = 101;
+
+        /// <summary>
+        /// parses a drawing consisting of line objects, given as a file in the application specific .isad format
+        /// </summary>
+        /// <param name="fileName">the path of the input file</param>
+        /// <returns>the width and height of the left canvas and the parsed picture as a list of lines</returns>
+        public Tuple<int, int, List<InternalLine>> ParseISADInputFile(String fileName)
+        {
+            return ParseISADInput(System.IO.File.ReadAllLines(fileName));
+        }
+
+        /// <summary>
+        /// parses a drawing consisting of line objects, given as the content of a .isad file, seperated into lines
+        /// </summary>
+        /// <param name="allLines">an array holding all lines of the input file</param>
+        /// <returns>the width and height of the left canvas and the parsed picture as a list of lines</returns>
+        private Tuple<int, int, List<InternalLine>> ParseISADInput(String[] allLines)
+        {
+            if (allLines.Length == 0)
+            {
+                throw new FileImporterException("file is empty", "", -1);
+            }
+            if (!"drawing".Equals(allLines[0]))
+            {
+                throw new FileImporterException("file is not an interactive sketch assistant drawing", ".isad files have to start with the 'drawing' token", 1);
+            }
+            if (!"enddrawing".Equals(allLines[allLines.Length - 1]))
+            {
+                throw new FileImporterException("unterminated drawing definition", ".isad files have to end with the 'enddrawing' token", allLines.Length);
+            }
+            Tuple<int, int> dimensions = ParseISADHeader(allLines);
+            List<InternalLine> picture = ParseISADBody(allLines, dimensions.Item1, dimensions.Item2);
+            
+            return new Tuple<int, int, List<InternalLine>>(dimensions.Item1, dimensions.Item2, picture);
+        }
+
+
+
+        /// <summary>
+        /// parses the first two lines of an input file in .isad format
+        /// </summary>
+        /// <param name="allLines">the input file as an array of lines</param>
+        /// <returns>the width and height of the left canvas</returns>
+        private Tuple<int, int> ParseISADHeader(String[] allLines)
+        {
+            int width;
+            int height;
+            if (!(allLines.Length > 1) || !Regex.Match(allLines[1], @"^\d+x?\d+$", RegexOptions.None).Success)
+            {
+                throw new FileImporterException("invalid or missing canvas size definition", "format: [width]x[heigth]", 2);
+            }
+            String[] size = allLines[1].Split('x');
+            width = Convert.ToInt32(size[0]);
+            height = Convert.ToInt32(size[1]);
+            return new Tuple<int, int>(width, height);
+        }
+
+        /// <summary>
+        /// parses all line entries of an input file in .isad format
+        /// </summary>
+        /// <param name="allLines">the input file as an array of lines</param>
+        /// <returns>the parsed picture as a list of lines</returns>
+        private List<InternalLine> ParseISADBody(String[] allLines, int width, int height)
+        {
+            String lineStartString = "line";
+            String lineEndString = "endline";
+            List<InternalLine> drawing = new List<InternalLine>();
+            //number of the line currently being parsed, enumeration starting at 0, body starts at the third line, therefore lin number 2
+            int i = 2;
+            //parse 'line' token and complete line definition
+            int lineStartPointer = i;
+            //holds the line number of the next expected beginning of a line definition, or of the enddrawing token
+            while (lineStartString.Equals(allLines[i]))
+            {
+                //start parsing next line
+                i++;
+                List<Point> newLine = new List<Point>();
+                while (!lineEndString.Equals(allLines[i]))
+                {
+                    if (i == allLines.Length)
+                    {
+                        throw new FileImporterException("unterminated line definition", null, (i + 1));
+                    }
+                    //parse single point definition
+                    if (!Regex.Match(allLines[i], @"^\d+;\d+$", RegexOptions.None).Success)
+                    {
+                        throw new FileImporterException("invalid Point definition: wrong format", "format: [xCoordinate];[yCoordinate]", (i + 1));
+                    }
+                    String[] coordinates = allLines[i].Split(';');
+                    //no errors possible, convertability to int already checked above
+                    int xCoordinate = Convert.ToInt32(coordinates[0]);
+                    int yCoordinate = Convert.ToInt32(coordinates[1]);
+                    if (xCoordinate < 0 || yCoordinate < 0 || xCoordinate > width - 1 || yCoordinate > height - 1)
+                    {
+                        throw new FileImporterException("invalid Point definition: point out of bounds", null, (i + 1));
+                    }
+                    newLine.Add(new Point(xCoordinate, yCoordinate));
+                    //start parsing next line
+                    i++;
+                }
+                //"parse" 'endline' token, syntax already checked at the beginning,  and start parsing next line
+                i++;
+                //add line to drawing
+                drawing.Add(new InternalLine(newLine));
+                //update lineStartPointer to the presumable start of the next line
+                lineStartPointer = i;
+            }
+            //check if end of body is reached after there are no more line definitions
+            if (i != allLines.Length - 1)
+            {
+                throw new FileImporterException("missing or invalid line definition token", "line definitions start with the 'line' token", (i + 1));
+            }
+            //return parsed picture
+            return drawing;
+        }
+
+        /// <summary>
+        /// connection point for testing use only: calls ParseISADInput(String[] allLines) and directly passes the given argument (effectively bypassing the File Input functionality)
+        /// </summary>
+        /// <param name="allLines">an array holding all lines of the input file</param>
+        /// <returns>the width and height of the left canvas and the parsed picture as a list of lines</returns>
+        public Tuple<int, int, List<InternalLine>> ParseISADInputForTesting(String[] allLines)
+        {
+            return ParseISADInput(allLines);
+        }
+
+        /// <summary>
+        /// parses a svg drawing, given as a .svg file
+        /// <para />several severe restrictions to the svg standard apply:
+        /// <para /> - width and heigth values must be integers
+        /// <para /> - the supported svg elements to be drawn must be placed on top level directly inside the 'svg' tag
+        /// <para /> - except for the global 'svg' tag, no hierarchical elements (elements which contain other svg elements) may exist. in other words: after an opening element tag no other opening element tag may occur before the closing tag of this element.
+        /// <para /> - lines in front of the (single) opening and after the (single) closing global svg tag will be ignored during parsing
+        /// <para /> - unsupported svg elements on top level will be ignored during parsing
+        /// <para /> - the input file must not contain empty lines
+        /// <para /> - all input files have to be manually tested and approved for use with this program by a developer or otherwise entitled personnel, otherwise no guarantee about correct and error-free parsing will be given
+        /// </summary>
+        /// <param name="fileName">the path of the input file</param>
+        /// <returns>the width and height of the left canvas and the parsed picture as a list of lines</returns>
+        public Tuple<int, int, List<InternalLine>> ParseSVGInputFile(String fileName, int windowWidth, int windowHeight)
+        {
+            return ParseSVGInput(System.IO.File.ReadAllLines(fileName), windowWidth, windowHeight);
+        }
+
+        /// <summary>
+        /// parses a svg drawing, given as the content of a .svg file, seperated into lines
+        /// </summary>
+        /// <param name="allLines">an array holding all lines of the input file</param>
+        /// <returns>the width and height of the left canvas and the parsed picture as a list of lines</returns>
+        private Tuple<int, int, List<InternalLine>> ParseSVGInput(String[] allLines, double windowWidth, double windowHeight)
+        {
+            i = 0; //reset line pointer
+            if (allLines.Length == 0) //check for empty file
+            {
+                throw new FileImporterException("file is empty", "", -1);
+            }
+            var sizedef = ParseSVGHeader(allLines); //parse svg file header and get internal coordinate range
+            i++;
+            int width; //width of the resulting picture in pixels
+            int height; //height of the resulting picture in pixels
+            if(windowWidth != 0 && windowHeight != 0)
+            {
+                if (windowWidth / windowHeight > sizedef.Item1 / sizedef.Item2) //height dominant, width has to be smaller than drawing window to preserve xy-scale
+                {
+                    scale = windowHeight / sizedef.Item2;
+                    height = (int)Math.Round(windowHeight);
+                    width = (int) Math.Round(scale * sizedef.Item1);
+                }
+                else //width dominant, height has to be smaller than drawing window to preserve xy-scale
+                {
+                    scale = windowWidth / sizedef.Item1;
+                    width = (int)Math.Round(windowWidth);
+                    height = (int)Math.Round(scale * sizedef.Item2);
+                }
+            }
+            else
+            {
+                scale = 1;
+                width = sizedef.Item1;
+                height = sizedef.Item2;
+            }
+            for (int j=0; j < allLines.Length; j++)
+            {
+                allLines[j] = allLines[j].Trim(whitespaces);
+            }
+            List<InternalLine> picture = ParseSVGBody(allLines); //parse whole svg drawing into list of lines
+            return new Tuple<int, int, List<InternalLine>>(width, height, picture);
+        }
+
+        /// <summary>
+        /// parses the svg file header and returns the internal coordinate range of this drawing, and iterates i to point to the start of svg element definitions
+        /// </summary>
+        /// <param name="allLines">an array holding all lines of the input file</param>
+        /// <returns>the internal coordinate range of this drawing</returns>
+        private Tuple<int, int> ParseSVGHeader(String[] allLines)
+        {
+            while (!allLines[i].StartsWith("<svg")) //skip non-relevant metadata at start of svg file
+            {
+                i++;
+            }
+            String[] currentLine = allLines[i].Split(' ');
+            int width= -1;
+            int height= -1;
+            for(int j= 0; j < currentLine.Length; j++)
+            {
+                if (currentLine[j].StartsWith("width"))
+                {
+                    width = Convert.ToInt32(ParseSingleSVGAttribute(currentLine[j]));
+                }
+                else if (currentLine[j].StartsWith("height"))
+                {
+                    height = Convert.ToInt32(ParseSingleSVGAttribute(currentLine[j]));
+                }
+            }
+            if(width == -1)
+            {
+                throw new FileImporterException("missing width definition in SVG header", "the header should contain the \"width=...\" attribute", i+1);
+            }
+            if (height == -1)
+            {
+                throw new FileImporterException("missing height definition in SVG header", "the header should contain the \"height=...\" attribute", i + 1);
+            }
+            return new Tuple<int, int>(width, height);
+        }
+
+        /// <summary>
+        /// parses all relevant svg element definitions and skips the ones not representable by the sketch assistant
+        /// </summary>
+        /// <param name="allLines">an array holding all lines of the input file</param>
+        /// <returns>the parsed picture as a list of lines</returns>
+        private List<InternalLine> ParseSVGBody(String[] allLines)
+        {
+            List<InternalLine> picture = new List<InternalLine>();
+            while (!allLines[i].StartsWith("</svg"))
+            {
+                List<InternalLine> element = ParseSingleSVGElement(allLines);
+                if (element != null)
+                {
+                    picture.AddRange(element);
+                }
+                i++;
+                if (i > allLines.Length - 1) throw new FileImporterException("unterminated input file: missing </svg> tag", "the file must not contain empty lines", i + 1);
+            }
+            return picture;
+        }
+
+        /// <summary>
+        /// parses one toplevel svg element
+        /// </summary>
+        /// <param name="allLines">an array holding all lines of the input file</param>
+        /// <returns>the parsed Element as a list of lines</returns>
+        private List<InternalLine> ParseSingleSVGElement(string[] allLines)
+        {
+            String[] currentElement = GetCurrentElement(allLines);
+            return ParseSingleLineSVGElement(currentElement);
+        }
+
+        /// <summary>
+        /// parses a single toplevel svg element only taking one line
+        /// </summary>
+        /// <param name="allLines">an array holding all lines of the input file</param>
+        /// <returns>the parsed element as a Line object, or null if the element is not supported</returns>
+        private List<InternalLine> ParseSingleLineSVGElement(string[] currentElement)
+        {
+            List<Point> points= null;
+            List<InternalLine> element = null;
+            switch (currentElement[0])
+            {
+                case "<rect":
+                    points = ParseRect(currentElement);
+                    break;
+                case "<circle":
+                    points = ParseCircle(currentElement);
+                    break;
+                case "<ellipse":
+                    points = ParseEllipse(currentElement);
+                    break;
+                case "<line":
+                    points = ParseLine(currentElement);
+                    break;
+                case "<polyline":
+                    points = ParsePolyline(currentElement);
+                    break;
+                case "<polygon":
+                    points = ParsePolygon(currentElement);
+                    break;
+                case "<path":
+                    element = ParsePath(currentElement);
+                    break;
+                default: //unsupported svg element
+                    return null; //simply ignore
+            }
+            if (element == null)
+            {
+                element = new List<InternalLine>();
+                element.Add(new InternalLine(points));
+            }
+            return element;
+        }
+
+        /// <summary>
+        /// parses a rectangle definition into a List of Points representing a single line around the rectangle (in clockwise direction), ignoring rounded corners
+        /// </summary>
+        /// <param name="currentElement">the definition of the element as whitespace seperated String[]</param>
+        /// <returns>the parsed element as a List of Points</returns>
+        private List<Point> ParseRect(string[] currentElement)
+        {
+            double x = 0;
+            double y = 0;
+            double w = 0;
+            double h = 0;
+            double rx = 0;
+            double ry = 0;
+            for(int j= 0; j < currentElement.Length; j++)
+            {
+                if (currentElement[j].StartsWith("x="))
+                {
+                    x = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
+                }
+                else if (currentElement[j].StartsWith("y="))
+                {
+                    y = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
+                }
+                else if (currentElement[j].StartsWith("width="))
+                {
+                    w = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
+                }
+                else if (currentElement[j].StartsWith("height="))
+                {
+                    h = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
+                }
+                else if (currentElement[j].StartsWith("rx="))
+                {
+                    rx = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
+                }
+                else if (currentElement[j].StartsWith("ry="))
+                {
+                    ry = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
+                }
+            }
+            List<Point> rect = new List<Point>();
+            rect.Add(ScaleAndCreatePoint(x, y));
+            rect.Add(ScaleAndCreatePoint(x + w, y));
+            rect.Add(ScaleAndCreatePoint(x + w, y + h));
+            rect.Add(ScaleAndCreatePoint(x, y + h));
+            rect.Add(ScaleAndCreatePoint(x, y));
+            return rect;
+        }
+
+        /// <summary>
+        /// parses a circle definition into a List of Points
+        /// </summary>
+        /// <param name="currentElement">the definition of the element as whitespace seperated String[]</param>
+        /// <returns>the parsed element as a List of Points</returns>
+        private List<Point> ParseCircle(string[] currentElement)
+        {
+            double x = 0;
+            double y = 0;
+            double r = 0;
+            for (int j = 0; j < currentElement.Length; j++)
+            {
+                if (currentElement[j].StartsWith("cx="))
+                {
+                    x = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
+                }
+                else if (currentElement[j].StartsWith("cy="))
+                {
+                    y = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
+                }
+                else if (currentElement[j].StartsWith("r="))
+                {
+                    r = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
+                }
+            }
+            return SampleEllipse(x, y, r, r);
+        }
+
+        /// <summary>
+        /// parses a ellipse definition into a List of Points
+        /// </summary>
+        /// <param name="currentElement">the definition of the element as whitespace seperated String[]</param>
+        /// <returns>the parsed element as a List of Points</returns>
+        private List<Point> ParseEllipse(string[] currentElement)
+        {
+            double x = 0;
+            double y = 0;
+            double rx = 0;
+            double ry = 0;
+            for (int j = 0; j < currentElement.Length; j++)
+            {
+                if (currentElement[j].StartsWith("cx="))
+                {
+                    x = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
+                }
+                else if (currentElement[j].StartsWith("cy="))
+                {
+                    y = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
+                }
+                else if (currentElement[j].StartsWith("rx="))
+                {
+                    rx = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
+                }
+                else if (currentElement[j].StartsWith("ry="))
+                {
+                    ry = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
+                }
+            }
+            return SampleEllipse(x, y, rx, ry);
+        }
+
+        /// <summary>
+        /// parses a line definition into a List of two Points
+        /// </summary>
+        /// <param name="currentElement">the definition of the element as whitespace seperated String[]</param>
+        /// <returns>the parsed element as a List of Points</returns>
+        private List<Point> ParseLine(string[] currentElement)
+        {
+            double x1 = 0;
+            double y1 = 0;
+            double x2 = 0;
+            double y2 = 0;
+            for (int j = 0; j < currentElement.Length; j++)
+            {
+                if (currentElement[j].StartsWith("x1="))
+                {
+                    x1 = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
+                }
+                else if (currentElement[j].StartsWith("y1="))
+                {
+                    y1 = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
+                }
+                else if (currentElement[j].StartsWith("x2="))
+                {
+                    x2 = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
+                }
+                else if (currentElement[j].StartsWith("y2="))
+                {
+                    y2 = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
+                }
+            }
+            List<Point> line = new List<Point>();
+            line.Add(ScaleAndCreatePoint(x1, y1));
+            line.Add(ScaleAndCreatePoint(x2, y2));
+            return line;
+        }
+
+        /// <summary>
+        /// parses a polyline definition into a List of Points
+        /// </summary>
+        /// <param name="currentElement">the definition of the element as whitespace seperated String[]</param>
+        /// <returns>the parsed element as a List of Points</returns>
+        private List<Point> ParsePolyline(string[] currentElement)
+        {
+            String[] points = null;
+            for (int j = 0; j < currentElement.Length; j++)
+            {
+                if (currentElement[j].StartsWith("points="))
+                {
+                    List<String> pointDefs = new List<string>();
+                    pointDefs.Add(currentElement[j].Substring(8)); //parse first point coordinates by removing 'points="'
+                    j++;
+                    while (!currentElement[j].EndsWith("\""))
+                    {
+                        pointDefs.Add(currentElement[j]); //parse intermediate point coordinates
+                        j++;
+                    }
+                    pointDefs.Add(currentElement[j].Trim('"')); //parse last point coordinates by removing '"'
+                    points = pointDefs.ToArray();
+                }
+            }
+            List<Point> polyline = new List<Point>();
+            for (int k = 0; k < points.Length - 1; k += 2)
+            {
+                polyline.Add(ScaleAndCreatePoint(Convert.ToDouble(points[k], CultureInfo.InvariantCulture), Convert.ToDouble(points[k + 1], CultureInfo.InvariantCulture)));
+            }
+            return polyline;
+        }
+
+        /// <summary>
+        /// parses a polygon definition into a List of Points
+        /// </summary>
+        /// <param name="currentElement">the definition of the element as whitespace seperated String[]</param>
+        /// <returns>the parsed element as a List of Points</returns>
+        private List<Point> ParsePolygon(string[] currentElement)
+        {
+            String[] points = null;
+            for (int j = 0; j < currentElement.Length; j++)
+            {
+                if (currentElement[j].StartsWith("points="))
+                {
+                    List<String> pointDefs = new List<string>();
+                    pointDefs.Add(currentElement[j].Substring(8)); //parse first point coordinates by removing 'points="'
+                    j++;
+                    while (!currentElement[j].EndsWith("\""))
+                    {
+                        pointDefs.Add(currentElement[j]); //parse intermediate point coordinates
+                        j++;
+                    }
+                    pointDefs.Add(currentElement[j].Trim('"')); //parse last point coordinates by removing '"'
+                    points = pointDefs.ToArray();
+                }
+            }
+            List<Point> polygon = new List<Point>();
+            for (int k = 0; k < points.Length - 1; k+=2)
+            {
+                polygon.Add(ScaleAndCreatePoint(Convert.ToDouble(points[k], CultureInfo.InvariantCulture), Convert.ToDouble(points[k+1], CultureInfo.InvariantCulture)));
+            }
+            polygon.Add(ScaleAndCreatePoint(Convert.ToDouble(points[0], CultureInfo.InvariantCulture), Convert.ToDouble(points[1], CultureInfo.InvariantCulture))); //close polygon
+            return polygon;
+        }
+
+        /// <summary>
+        /// parses a path definition into a List of Points
+        /// </summary>
+        /// <param name="currentElement">the definition of the element as whitespace seperated String[]</param>
+        /// <returns>the parsed element as a List of Points</returns>
+        private List<InternalLine> ParsePath(string[] currentElement)
+        {
+            List<String> pathElements = new List<string>();
+            for (int j = 0; j < currentElement.Length; j++)
+            {
+                if (currentElement[j].StartsWith("d="))
+                {
+                    pathElements.Add(currentElement[j].Substring(3)); //parse first path element by removing 'd="'
+                    j++;
+                    while (!currentElement[j].EndsWith("\""))
+                    {
+                        pathElements.Add(currentElement[j]); //parse intermediate path element
+                        j++;
+                    }
+                    pathElements.Add(currentElement[j].Trim('"')); //parse last path element by removing '"'
+                }
+            }
+            NormalizePathDeclaration(pathElements); //expand path data to always explicitly have the command descriptor in front of the appropriate number of arguments and to seperate command descriptors, coordinates and other tokens always into seperate list elements (equivalent to seperation with spaces in the input file, but svg allows also for comma as a seperator, and for omitting seperators where possible without losing information (refer to svg grammer) to reduce file size
+            List<InternalLine> element = new List<InternalLine>();
+            List<Point> currentLine = new List<Point>();
+            double lastBezierControlPointX= 0;
+            double lastBezierControlPointY= 0;
+            double lastPositionX;
+            double lastPositionY;
+            double initialPositionX= -1;
+            double initialPositionY= -1;
+            bool newSubpath = true;
+            Tuple<List<Point>, double, double> valuesArc; //list of points, new values for: lastPositionX, lastPositionY
+            Tuple<List<Point>, double, double, double, double> valuesBezierCurve; //list of points, new values for: lastPositionX, lastPositionY, lastBezierControlPointX, lastBezierControlPointY
+            var valuesSinglePoint = Parse_M_L(pathElements); //new point, new values for: lastPositionX, lastPositionY
+            currentLine = new List<Point>();
+            currentLine.Add(valuesSinglePoint.Item1);
+            lastPositionX = valuesSinglePoint.Item2;
+            lastPositionY = valuesSinglePoint.Item3;
+            String currentToken;
+            while (!(pathElements.Count == 0)){
+                if (newSubpath)
+                {
+                    initialPositionX = lastPositionX; //update buffers for coordinates of first point of active subpath
+                    initialPositionY = lastPositionY;
+                    newSubpath = false;
+                }
+                currentToken = pathElements.First();
+                if (currentToken.Equals("M"))
+                {
+                    element.Add(new InternalLine(currentLine)); //save current line
+                    valuesSinglePoint = Parse_M_L(pathElements);
+                    currentLine = new List<Point>(); //create new empty line
+                    currentLine.Add(valuesSinglePoint.Item1); //add point to new line
+                    lastPositionX = valuesSinglePoint.Item2; //save last point coordinates
+                    lastPositionY = valuesSinglePoint.Item3; //save last point coordinates
+                }
+                else if (currentToken.Equals("m"))
+                {
+                    element.Add(new InternalLine(currentLine)); //save current line
+                    valuesSinglePoint = Parse_m_l(pathElements, lastPositionX, lastPositionY);
+                    currentLine = new List<Point>(); //create new empty line
+                    currentLine.Add(valuesSinglePoint.Item1); //add point to new line
+                    lastPositionX = valuesSinglePoint.Item2; //save last point coordinates
+                    lastPositionY = valuesSinglePoint.Item3; //save last point coordinates
+                }
+                else if (currentToken.Equals("Z") || currentToken.Equals("z"))
+                {
+                    valuesSinglePoint = Parse_Z(pathElements, initialPositionX, initialPositionY); //method call only used for uniform program structure... only real effect of method is to consume one token
+                    newSubpath = true;
+                    currentLine.Add(valuesSinglePoint.Item1); //add point to old line
+                    element.Add(new InternalLine(currentLine)); //save current line
+                    currentLine = new List<Point>(); //create new empty line
+                    currentLine.Add(valuesSinglePoint.Item1); //add point to new line
+                    lastPositionX = valuesSinglePoint.Item2; //save last point coordinates
+                    lastPositionY = valuesSinglePoint.Item3; //save last point coordinates
+                }
+                else if (currentToken.Equals("L"))
+                {
+                    valuesSinglePoint = Parse_M_L(pathElements);
+                    currentLine.Add(valuesSinglePoint.Item1); //add point to new line
+                    lastPositionX = valuesSinglePoint.Item2; //save last point coordinates
+                    lastPositionY = valuesSinglePoint.Item3; //save last point coordinates
+                }
+                else if (currentToken.Equals("l"))
+                {
+                    valuesSinglePoint = Parse_m_l(pathElements, lastPositionX, lastPositionY);
+                    currentLine.Add(valuesSinglePoint.Item1); //add point to new line
+                    lastPositionX = valuesSinglePoint.Item2; //save last point coordinates
+                    lastPositionY = valuesSinglePoint.Item3; //save last point coordinates
+                }
+                else if (currentToken.Equals("H"))
+                {
+                    valuesSinglePoint = Parse_H(pathElements, lastPositionY);
+                    currentLine.Add(valuesSinglePoint.Item1); //add point to new line
+                    lastPositionX = valuesSinglePoint.Item2; //save last point coordinates
+                    lastPositionY = valuesSinglePoint.Item3; //save last point coordinates
+                }
+                else if (currentToken.Equals("h"))
+                {
+                    valuesSinglePoint = Parse_h(pathElements, lastPositionX, lastPositionY);
+                    currentLine.Add(valuesSinglePoint.Item1); //add point to new line
+                    lastPositionX = valuesSinglePoint.Item2; //save last point coordinates
+                    lastPositionY = valuesSinglePoint.Item3; //save last point coordinates
+                }
+                else if (currentToken.Equals("V"))
+                {
+                    valuesSinglePoint = Parse_V(pathElements, lastPositionX);
+                    currentLine.Add(valuesSinglePoint.Item1); //add point to new line
+                    lastPositionX = valuesSinglePoint.Item2; //save last point coordinates
+                    lastPositionY = valuesSinglePoint.Item3; //save last point coordinates
+                }
+                else if (currentToken.Equals("v"))
+                {
+                    valuesSinglePoint = Parse_v(pathElements, lastPositionX, lastPositionY);
+                    currentLine.Add(valuesSinglePoint.Item1); //add point to new line
+                    lastPositionX = valuesSinglePoint.Item2; //save last point coordinates
+                    lastPositionY = valuesSinglePoint.Item3; //save last point coordinates
+                }
+                else if (currentToken.Equals("C"))
+                {
+                    valuesBezierCurve = Parse_C(pathElements, lastPositionX, lastPositionY);
+                    currentLine.AddRange(valuesBezierCurve.Item1); //add point to new line
+                    lastPositionX = valuesBezierCurve.Item2; //save last point coordinates
+                    lastPositionY = valuesBezierCurve.Item3; //save last point coordinates
+                    lastBezierControlPointX = valuesBezierCurve.Item4; //save last bezier control point coordinates
+                    lastBezierControlPointY = valuesBezierCurve.Item5; //save last bezier control point coordinates
+                }
+                else if (currentToken.Equals("c"))
+                {
+                    valuesBezierCurve = Parse_C(pathElements, lastPositionX, lastPositionY);
+                    currentLine.AddRange(valuesBezierCurve.Item1); //add point to new line
+                    lastPositionX = valuesBezierCurve.Item2; //save last point coordinates
+                    lastPositionY = valuesBezierCurve.Item3; //save last point coordinates
+                    lastBezierControlPointX = valuesBezierCurve.Item4; //save last bezier control point coordinates
+                    lastBezierControlPointY = valuesBezierCurve.Item5; //save last bezier control point coordinates
+                }
+                else if (currentToken.Equals("S"))
+                {
+                    valuesBezierCurve = Parse_S(pathElements, lastPositionX, lastPositionY, lastBezierControlPointX, lastBezierControlPointY);
+                    currentLine.AddRange(valuesBezierCurve.Item1); //add point to new line
+                    lastPositionX = valuesBezierCurve.Item2; //save last point coordinates
+                    lastPositionY = valuesBezierCurve.Item3; //save last point coordinates
+                    lastBezierControlPointX = valuesBezierCurve.Item4; //save last bezier control point coordinates
+                    lastBezierControlPointY = valuesBezierCurve.Item5; //save last bezier control point coordinates
+                }
+                else if (currentToken.Equals("s"))
+                {
+                    valuesBezierCurve = Parse_s(pathElements, lastPositionX, lastPositionY, lastBezierControlPointX, lastBezierControlPointY);
+                    currentLine.AddRange(valuesBezierCurve.Item1); //add point to new line
+                    lastPositionX = valuesBezierCurve.Item2; //save last point coordinates
+                    lastPositionY = valuesBezierCurve.Item3; //save last point coordinates
+                    lastBezierControlPointX = valuesBezierCurve.Item4; //save last bezier control point coordinates
+                    lastBezierControlPointY = valuesBezierCurve.Item5; //save last bezier control point coordinates
+                }
+                else if (currentToken.Equals("Q"))
+                {
+                    valuesBezierCurve = Parse_Q(pathElements, lastPositionX, lastPositionY);
+                    currentLine.AddRange(valuesBezierCurve.Item1); //add point to new line
+                    lastPositionX = valuesBezierCurve.Item2; //save last point coordinates
+                    lastPositionY = valuesBezierCurve.Item3; //save last point coordinates
+                    lastBezierControlPointX = valuesBezierCurve.Item4; //save last bezier control point coordinates
+                    lastBezierControlPointY = valuesBezierCurve.Item5; //save last bezier control point coordinates
+                }
+                else if (currentToken.Equals("q"))
+                {
+                    valuesBezierCurve = Parse_q(pathElements, lastPositionX, lastPositionY);
+                    currentLine.AddRange(valuesBezierCurve.Item1); //add point to new line
+                    lastPositionX = valuesBezierCurve.Item2; //save last point coordinates
+                    lastPositionY = valuesBezierCurve.Item3; //save last point coordinates
+                    lastBezierControlPointX = valuesBezierCurve.Item4; //save last bezier control point coordinates
+                    lastBezierControlPointY = valuesBezierCurve.Item5; //save last bezier control point coordinates
+                }
+                else if (currentToken.Equals("T"))
+                {
+                    valuesBezierCurve = Parse_T(pathElements, lastPositionX, lastPositionY, lastBezierControlPointX, lastBezierControlPointY);
+                    currentLine.AddRange(valuesBezierCurve.Item1); //add point to new line
+                    lastPositionX = valuesBezierCurve.Item2; //save last point coordinates
+                    lastPositionY = valuesBezierCurve.Item3; //save last point coordinates
+                    lastBezierControlPointX = valuesBezierCurve.Item4; //save last bezier control point coordinates
+                    lastBezierControlPointY = valuesBezierCurve.Item5; //save last bezier control point coordinates
+                }
+                else if (currentToken.Equals("t"))
+                {
+                    valuesBezierCurve = Parse_t(pathElements, lastPositionX, lastPositionY, lastBezierControlPointX, lastBezierControlPointY);
+                    currentLine.AddRange(valuesBezierCurve.Item1); //add point to new line
+                    lastPositionX = valuesBezierCurve.Item2; //save last point coordinates
+                    lastPositionY = valuesBezierCurve.Item3; //save last point coordinates
+                    lastBezierControlPointX = valuesBezierCurve.Item4; //save last bezier control point coordinates
+                    lastBezierControlPointY = valuesBezierCurve.Item5; //save last bezier control point coordinates
+                }
+                else if (currentToken.Equals("A"))
+                {
+                    valuesArc = Parse_A(pathElements, lastPositionX, lastPositionY);
+                    currentLine.AddRange(valuesArc.Item1); //add points to new line
+                    lastPositionX = valuesArc.Item2; //save last point coordinates
+                    lastPositionY = valuesArc.Item3; //save last point coordinates
+                }
+                else if (currentToken.Equals("a"))
+                {
+                    valuesArc = Parse_a(pathElements, lastPositionX, lastPositionY);
+                    currentLine.AddRange(valuesArc.Item1); //add points to new line
+                    lastPositionX = valuesArc.Item2; //save last point coordinates
+                    lastPositionY = valuesArc.Item3; //save last point coordinates
+                }
+                else
+                {
+                    throw new FileImporterException("invalid path argument or path data formatting: read argument " + pathElements.First(), "valid path arguments are: {M,Z,L,H,V,C,S,Q,T,A} in upper and lower case", i + 1);
+                }
+            }
+            if (currentLine.Count > 1)
+            {
+                element.Add(new InternalLine(currentLine)); //save current line
+            }
+            return element;
+        }
+
+        /// <summary>
+        /// normalizes the declaration of the data field of a path declaration by splitting coordinates still connected by a semicolon and command descriptors which are directly attached to the following coordinate into seperate tokens, also repeats omitted command descriptor tokens when the same command is repeated multiple times
+        /// </summary>
+        /// <param name="pathElements">the list of tokens to normalize, by splitting up existing tokens and adding new command descriptor tokens</param>
+        private void NormalizePathDeclaration(List<string> pathElements)
+        {
+            Char lastCommand = 'M';
+            int argumentCounter = 0;
+            for( int j= 0; j < pathElements.Count; j++)
+            {
+                String currentElement = pathElements.ElementAt(j);
+                if (currentElement.Length != 1)
+                {
+                    if (((currentElement.First() >= 'A' && currentElement.First() <= 'Z') || (currentElement.First() >= 'a' && currentElement.First() <= 'z')) && currentElement.First() != 'e') //seperate a single command descriptor / letter
+                    {
+                        pathElements.RemoveAt(j);
+                        pathElements.Insert(j, currentElement.First() + ""); //insert letter as seperate element
+                        pathElements.Insert(j + 1, currentElement.Substring(1)); //insert rest of String at next position so it will be processed again
+                        lastCommand = currentElement.First();
+                        argumentCounter = 0;
+                    }
+                    else if ((currentElement.First() >= '0' && currentElement.First() <= '9') || currentElement.First() == '-' || currentElement.First() == '+' || currentElement.First() != 'e') //seperate a single coordinate / number
+                    {
+                        bool repeatCommandDescriptor = false; 
+                        switch (lastCommand){ //check for reaching of next command with omitted command descriptor
+                            case 'M':
+                                if (argumentCounter >= 2) repeatCommandDescriptor = true;
+                                break;
+                            case 'm':
+                                if (argumentCounter >= 2) repeatCommandDescriptor = true;
+                                break;
+                            case 'L':
+                                if (argumentCounter >= 2) repeatCommandDescriptor = true;
+                                break;
+                            case 'l':
+                                if (argumentCounter >= 2) repeatCommandDescriptor = true;
+                                break;
+                            case 'V':
+                                if (argumentCounter >= 1) repeatCommandDescriptor = true;
+                                break;
+                            case 'v':
+                                if (argumentCounter >= 1) repeatCommandDescriptor = true;
+                                break;
+                            case 'H':
+                                if (argumentCounter >= 1) repeatCommandDescriptor = true;
+                                break;
+                            case 'h':
+                                if (argumentCounter >= 1) repeatCommandDescriptor = true;
+                                break;
+                            case 'C':
+                                if (argumentCounter >= 6) repeatCommandDescriptor = true;
+                                break;
+                            case 'c':
+                                if (argumentCounter >= 6) repeatCommandDescriptor = true;
+                                break;
+                            case 'S':
+                                if (argumentCounter >= 4) repeatCommandDescriptor = true;
+                                break;
+                            case 's':
+                                if (argumentCounter >= 4) repeatCommandDescriptor = true;
+                                break;
+                            case 'Q':
+                                if (argumentCounter >= 4) repeatCommandDescriptor = true;
+                                break;
+                            case 'q':
+                                if (argumentCounter >= 4) repeatCommandDescriptor = true;
+                                break;
+                            case 'T':
+                                if (argumentCounter >= 2) repeatCommandDescriptor = true;
+                                break;
+                            case 't':
+                                if (argumentCounter >= 2) repeatCommandDescriptor = true;
+                                break;
+                            case 'A':
+                                if (argumentCounter >= 7) repeatCommandDescriptor = true;
+                                break;
+                            case 'a':
+                                if (argumentCounter >= 7) repeatCommandDescriptor = true;
+                                break;
+                        }
+                        if (repeatCommandDescriptor)
+                        {
+                            pathElements.Insert(j, lastCommand + ""); //repeat command descriptor
+                            j++; //skip command descriptor (was put into active position in the list
+                            argumentCounter = 0; //reset argument counter
+                        }
+                        bool decimalPointEncountered = false;
+                        for (int k = 1; k < currentElement.Length; k++)
+                        {
+                            if (!decimalPointEncountered && currentElement.ElementAt(k) == '.') //allow up to one decimal point in numbers
+                            {
+                                decimalPointEncountered = true;
+                            }
+                            else if (!((currentElement.ElementAt(k) >= '0' && currentElement.ElementAt(k) <= '9') || currentElement.First() == '-' || currentElement.First() == '+' || currentElement.First() != 'e'))
+                            {
+                                pathElements.RemoveAt(j);
+                                pathElements.Insert(j, currentElement.Substring(0, k - 1)); //insert number as seperate element
+                                pathElements.Insert(j + 1, currentElement.Substring(k)); //insert rest of String at next position so it will be processed again
+                                break;
+                            }
+                        }
+                        argumentCounter++; 
+                    }
+                    else //parse non-space seperators and skip other unsupported characters (the only other valid ones per svg standard would be weird tokens looking like format descriptors (e.g. '#xC'), these are unsopported and will likely cause an error or other inconsitencies during parsing)
+                    {
+                        for (int k = 1; k < currentElement.Length; k++)
+                        {
+                            if (((currentElement.ElementAt(k) >= '0' && currentElement.ElementAt(k) <= '9')) || currentElement.ElementAt(k) == '-' || currentElement.ElementAt(k) == '+' || (currentElement.ElementAt(k) >= 'A' && currentElement.ElementAt(k) <= 'Z') || (currentElement.ElementAt(k) >= 'a' && currentElement.ElementAt(k) <= 'z'))
+                            {
+                                pathElements.RemoveAt(j);
+                                pathElements.Insert(j + 1, currentElement.Substring(k)); //insert rest of String at next position so it will be processed again
+                                break;
+                            }
+                        }
+                    }
+                }
+                else
+                {
+                    if ((currentElement.First() >= 'A' && currentElement.First() <= 'Z') || (currentElement.First() >= 'a' && currentElement.First() <= 'z')) //update lastCommand buffer when reading single letter
+                    {
+                        lastCommand = currentElement.First();
+                        argumentCounter = 0;
+                    }
+                    else if(!(currentElement.First() >= '0' && currentElement.First() <= '9')) //not a number
+                    {
+                        pathElements.RemoveAt(j); //remove element
+                        j--; //decrement index pointer so next element will not be skipped (indices of all folowing elements just decreased by 1)
+                    }
+                    else //a single digit number
+                    {
+                        bool repeatCommandDescriptor = false;
+                        switch (lastCommand)
+                        { //check for reaching of next command with omitted command descriptor
+                            case 'M':
+                                if (argumentCounter >= 2) repeatCommandDescriptor = true;
+                                break;
+                            case 'm':
+                                if (argumentCounter >= 2) repeatCommandDescriptor = true;
+                                break;
+                            case 'L':
+                                if (argumentCounter >= 2) repeatCommandDescriptor = true;
+                                break;
+                            case 'l':
+                                if (argumentCounter >= 2) repeatCommandDescriptor = true;
+                                break;
+                            case 'V':
+                                if (argumentCounter >= 1) repeatCommandDescriptor = true;
+                                break;
+                            case 'v':
+                                if (argumentCounter >= 1) repeatCommandDescriptor = true;
+                                break;
+                            case 'H':
+                                if (argumentCounter >= 1) repeatCommandDescriptor = true;
+                                break;
+                            case 'h':
+                                if (argumentCounter >= 1) repeatCommandDescriptor = true;
+                                break;
+                            case 'C':
+                                if (argumentCounter >= 6) repeatCommandDescriptor = true;
+                                break;
+                            case 'c':
+                                if (argumentCounter >= 6) repeatCommandDescriptor = true;
+                                break;
+                            case 'S':
+                                if (argumentCounter >= 4) repeatCommandDescriptor = true;
+                                break;
+                            case 's':
+                                if (argumentCounter >= 4) repeatCommandDescriptor = true;
+                                break;
+                            case 'Q':
+                                if (argumentCounter >= 4) repeatCommandDescriptor = true;
+                                break;
+                            case 'q':
+                                if (argumentCounter >= 4) repeatCommandDescriptor = true;
+                                break;
+                            case 'T':
+                                if (argumentCounter >= 2) repeatCommandDescriptor = true;
+                                break;
+                            case 't':
+                                if (argumentCounter >= 2) repeatCommandDescriptor = true;
+                                break;
+                            case 'A':
+                                if (argumentCounter >= 7) repeatCommandDescriptor = true;
+                                break;
+                            case 'a':
+                                if (argumentCounter >= 7) repeatCommandDescriptor = true;
+                                break;
+                        }
+                        if (repeatCommandDescriptor)
+                        {
+                            pathElements.Insert(j, lastCommand + ""); //repeat command descriptor
+                            j++; //skip command descriptor (was put into active position in the list
+                            argumentCounter = 0; //reset argument counter
+                        }
+                        argumentCounter++;
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// parses a "closeloop" path element
+        /// </summary>
+        /// <param name="pathElements">a list of all not yet parsed path element tokens and values in correct order, starting with the element to be parsed</param>
+        /// <param name="initialPositionX">absolute x coordinate of the last initial point of this subpath</param>
+        /// <param name="initialPositionY">absolute y coordinate of the last initial point of this subpath</param>
+        /// <returns></returns>
+        private Tuple<Point, double, double> Parse_Z(List<string> pathElements, double initialPositionX, double initialPositionY)
+        {
+            pathElements.RemoveAt(0); //remove element descriptor token
+            return new Tuple<Point, double, double>(ScaleAndCreatePoint(initialPositionX, initialPositionY), initialPositionX, initialPositionY);
+        }
+
+        /// <summary>
+        /// parses a "moveto", "close loop" or "lineto" path element with absolute coordinates
+        /// </summary>
+        /// <param name="pathElements">a list of all not yet parsed path element tokens and values in correct order, starting with the element to be parsed</param>
+        /// <returns>the point at the end of the move, close loop or line action and its exact, unscaled coordinates</returns>
+        private Tuple<Point, double, double> Parse_M_L(List<string> pathElements)
+        {
+            pathElements.RemoveAt(0); //remove element descriptor token
+            double x = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            double y = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            return new Tuple<Point, double, double>(ScaleAndCreatePoint(x, y), x, y);
+        }
+
+        /// <summary>
+        /// parses a "moveto", "close loop" or "lineto" path element with relative coordinates
+        /// </summary>
+        /// <param name="pathElements">a list of all not yet parsed path element tokens and values in correct order, starting with the element to be parsed</param>
+        /// <param name="lastPositionX">absolute x coordinate of the last active point</param>
+        /// <param name="lastPositionY">absolute y coordinate of the last active point</param>
+        /// <returns>the point at the end of the move, close loop or line action and its exact, unscaled coordinates</returns>
+        private Tuple<Point, double, double> Parse_m_l(List<string> pathElements, double lastPositionX, double lastPositionY)
+        {
+            pathElements.RemoveAt(0); //remove element descriptor token
+            double x = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse relative x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            double y = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse relative y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            x = lastPositionX + x; //compute absolute x coordinate
+            y = lastPositionY + y; //compute absolute y coordinate
+            return new Tuple<Point, double, double>(ScaleAndCreatePoint(x, y), x, y);
+        }
+
+        /// <summary>
+        /// parses a "horizontal lineto" path element with absolute coordinates
+        /// </summary>
+        /// <param name="pathElements">a list of all not yet parsed path element tokens and values in correct order, starting with the element to be parsed</param>
+        /// <param name="lastPositionY">absolute y coordinate of the last active point</param>
+        /// <returns>the point at the end of the horizontal line action and its exact, unscaled coordinates</returns>
+        private Tuple<Point, double, double> Parse_H(List<string> pathElements, double lastPositionY)
+        {
+            pathElements.RemoveAt(0); //remove element descriptor token
+            double x = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            return new Tuple<Point, double, double>(ScaleAndCreatePoint(x, lastPositionY), x, lastPositionY);
+        }
+
+        /// <summary>
+        /// parses a "horizontal lineto" path element with relative coordinates
+        /// </summary>
+        /// <param name="pathElements">a list of all not yet parsed path element tokens and values in correct order, starting with the element to be parsed</param>
+        /// <param name="lastPositionX">absolute x coordinate of the last active point</param>
+        /// <param name="lastPositionY">absolute y coordinate of the last active point</param>
+        /// <returns>the point at the end of the horizontal line action and its exact, unscaled coordinates</returns>
+        private Tuple<Point, double, double> Parse_h(List<string> pathElements, double lastPositionX, double lastPositionY)
+        {
+            pathElements.RemoveAt(0); //remove element descriptor token
+            double x = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse relative x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            x = lastPositionX + x; //compute absolute x coordinate
+            return new Tuple<Point, double, double>(ScaleAndCreatePoint(x, lastPositionY), x, lastPositionY);
+        }
+
+        /// <summary>
+        /// parses a "vertical lineto" path element with absolute coordinates
+        /// </summary>
+        /// <param name="pathElements">a list of all not yet parsed path element tokens and values in correct order, starting with the element to be parsed</param>
+        /// <param name="lastPositionX">absolute x coordinate of the last active point</param>
+        /// <returns>the point at the end of the vertical line action and its exact, unscaled coordinates</returns>
+        private Tuple<Point, double, double> Parse_V(List<string> pathElements, double lastPositionX)
+        {
+            pathElements.RemoveAt(0); //remove element descriptor token
+            double y = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            return new Tuple<Point, double, double>(ScaleAndCreatePoint(lastPositionX, y), lastPositionX, y);
+        }
+
+        /// <summary>
+        /// parses a "vertical lineto" path element with relative coordinates
+        /// </summary>
+        /// <param name="pathElements">a list of all not yet parsed path element tokens and values in correct order, starting with the element to be parsed</param>
+        /// <param name="lastPositionX">absolute x coordinate of the last active point</param>
+        /// <param name="lastPositionY">absolute y coordinate of the last active point</param>
+        /// <returns>the point at the end of the vertical line action and its exact, unscaled coordinates</returns>
+        private Tuple<Point, double, double> Parse_v(List<string> pathElements, double lastPositionX, double lastPositionY)
+        {
+            pathElements.RemoveAt(0); //remove element descriptor token
+            double y = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse relative y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            y = lastPositionY + y; //compute absolute y coordinate
+            return new Tuple<Point, double, double>(ScaleAndCreatePoint(lastPositionX, y), lastPositionX, y);
+        }
+
+        /// <summary>
+        /// parses a "cubic bezier curve" path element with absolute coordinates
+        /// </summary>
+        /// <param name="pathElements">a list of all not yet parsed path element tokens and values in correct order, starting with the element to be parsed</param>
+        /// <param name="lastPositionX">absolute x coordinate of the last active point</param>
+        /// <param name="lastPositionY">absolute y coordinate of the last active point</param>
+        /// <returns>a List of Points containing all sampled points on the bezier curve, aswell as the unscaled x and y coordinates of the last point of the curve and of the second bezier control point</returns>
+        private Tuple<List<Point>, double, double, double, double> Parse_C(List<string> pathElements, double lastPositionX, double lastPositionY)
+        {
+            pathElements.RemoveAt(0); //remove element descriptor token
+            double x1 = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse first control point x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            double y1 = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse first control point y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            double x2 = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse second control point x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            double y2 = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse second control point y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            double x = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse target point x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            double y = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse target point y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            return new Tuple<List<Point>, double, double, double, double>(SampleCubicBezier(lastPositionX, lastPositionY, x1, y1, x2, y2, x, y), x, y, x2, y2);
+        }
+
+        /// <summary>
+        /// parses a "cubic bezier curve" path element with relative coordinates
+        /// </summary>
+        /// <param name="pathElements">a list of all not yet parsed path element tokens and values in correct order, starting with the element to be parsed</param>
+        /// <param name="lastPositionX">absolute x coordinate of the last active point</param>
+        /// <param name="lastPositionY">absolute y coordinate of the last active point</param>
+        /// <returns>a List of Points containing all sampled points on the bezier curve, aswell as the unscaled x and y coordinates of the last point of the curve and of the second bezier control point</returns>
+        private Tuple<List<Point>, double, double, double, double> Parse_c(List<string> pathElements, double lastPositionX, double lastPositionY)
+        {
+            pathElements.RemoveAt(0); //remove element descriptor token
+            double x1 = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse first control point x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            double y1 = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse first control point y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            double x2 = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse second control point x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            double y2 = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse second control point y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            double x = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse target point x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            double y = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse target point y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            x1 = lastPositionX + x1; //compute absolute x coordinate
+            y1 = lastPositionY + y1; //compute absolute y coordinate
+            x2 = lastPositionX + x2; //compute absolute x coordinate
+            y2 = lastPositionY + y2; //compute absolute y coordinate
+            x = lastPositionX + x; //compute absolute x coordinate
+            y = lastPositionY + y; //compute absolute y coordinate
+            return new Tuple<List<Point>, double, double, double, double>(SampleCubicBezier(lastPositionX, lastPositionY, x1, y1, x2, y2, x, y), x, y, x2, y2);
+        }
+
+        /// <summary>
+        /// parses a "cubic bezier curve shorthand" path element with absolute coordinates
+        /// </summary>
+        /// <param name="pathElements">a list of all not yet parsed path element tokens and values in correct order, starting with the element to be parsed</param>
+        /// <param name="lastPositionX">absolute x coordinate of the last active point</param>
+        /// <param name="lastPositionY">absolute y coordinate of the last active point</param>
+        /// <param name="lastBezierControlPointX">absolute x coordinate of the last bezier control point of the previous bezier curve</param>
+        /// <param name="lastBezierControlPointY">absolute y coordinate of the last bezier control point of the previous bezier curve</param>
+        /// <returns>a List of Points containing all sampled points on the bezier curve, aswell as the unscaled x and y coordinates of the last point of the curve and of the second bezier control point</returns>
+        private Tuple<List<Point>, double, double, double, double> Parse_S(List<string> pathElements, double lastPositionX, double lastPositionY, double lastBezierControlPointX, double lastBezierControlPointY)
+        {
+            pathElements.RemoveAt(0); //remove element descriptor token
+            double x2 = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse second control point x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            double y2 = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse second control point y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            double x = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse target point x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            double y = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse target point y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            double x1 = lastPositionX + (lastPositionX - lastBezierControlPointX); //mirror last bezier control point at bezier start point to get first new bezier control point
+            double y1 = lastPositionY + (lastPositionY - lastBezierControlPointY); //mirror last bezier control point at bezier start point to get first new bezier control point
+            return new Tuple<List<Point>, double, double, double, double>(SampleCubicBezier(lastPositionX, lastPositionY, x1, y1, x2, y2, x, y), x, y, x2, y2);
+        }
+
+        /// <summary>
+        /// parses a "cubic bezier curve shorthand" path element with relative coordinates
+        /// </summary>
+        /// <param name="pathElements">a list of all not yet parsed path element tokens and values in correct order, starting with the element to be parsed</param>
+        /// <param name="lastPositionX">absolute x coordinate of the last active point</param>
+        /// <param name="lastPositionY">absolute y coordinate of the last active point</param>
+        /// <param name="lastBezierControlPointX">absolute x coordinate of the last bezier control point of the previous bezier curve</param>
+        /// <param name="lastBezierControlPointY">absolute y coordinate of the last bezier control point of the previous bezier curve</param>
+        /// <returns>a List of Points containing all sampled points on the bezier curve, aswell as the unscaled x and y coordinates of the last point of the curve and of the second bezier control point</returns>
+        private Tuple<List<Point>, double, double, double, double> Parse_s(List<string> pathElements, double lastPositionX, double lastPositionY, double lastBezierControlPointX, double lastBezierControlPointY)
+        {
+            pathElements.RemoveAt(0); //remove element descriptor token
+            double x2 = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse second control point x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            double y2 = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse second control point y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            double x = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse target point x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            double y = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse target point y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            double x1 = lastPositionX + (lastPositionX - lastBezierControlPointX); //mirror last bezier control point at bezier start point to get first new bezier control point
+            double y1 = lastPositionY + (lastPositionY - lastBezierControlPointY); //mirror last bezier control point at bezier start point to get first new bezier control point
+            x2 = lastPositionX + x2; //compute absolute x coordinate
+            y2 = lastPositionY + y2; //compute absolute y coordinate
+            x = lastPositionX + x; //compute absolute x coordinate
+            y = lastPositionY + y; //compute absolute y coordinate
+            return new Tuple<List<Point>, double, double, double, double>(SampleCubicBezier(lastPositionX, lastPositionY, x1, y1, x2, y2, x, y), x, y, x2, y2);
+        }
+
+        /// <summary>
+        /// parses a "quadratic bezier curve" path element with absolute coordinates
+        /// </summary>
+        /// <param name="pathElements">a list of all not yet parsed path element tokens and values in correct order, starting with the element to be parsed</param>
+        /// <param name="lastPositionX">absolute x coordinate of the last active point</param>
+        /// <param name="lastPositionY">absolute y coordinate of the last active point</param>
+        /// <returns>a List of Points containing all sampled points on the bezier curve, aswell as the unscaled x and y coordinates of the last point of the curve and of the bezier control point</returns>
+        private Tuple<List<Point>, double, double, double, double> Parse_Q(List<string> pathElements, double lastPositionX, double lastPositionY)
+        {
+            pathElements.RemoveAt(0); //remove element descriptor token
+            double x1 = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse control point x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            double y1 = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse control point y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            double x = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse target point x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            double y = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse target point y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            return new Tuple<List<Point>, double, double, double, double>(SampleQuadraticBezier(lastPositionX, lastPositionY, x1, y1, x, y), x, y, x1, y1);
+        }
+
+        /// <summary>
+        /// parses a "quadratic bezier curve" path element with relative coordinates
+        /// </summary>
+        /// <param name="pathElements">a list of all not yet parsed path element tokens and values in correct order, starting with the element to be parsed</param>
+        /// <param name="lastPositionX">absolute x coordinate of the last active point</param>
+        /// <param name="lastPositionY">absolute y coordinate of the last active point</param>
+        /// <returns>a List of Points containing all sampled points on the bezier curve, aswell as the unscaled x and y coordinates of the last point of the curve and of the bezier control point</returns>
+        private Tuple<List<Point>, double, double, double, double> Parse_q(List<string> pathElements, double lastPositionX, double lastPositionY)
+        {
+            pathElements.RemoveAt(0); //remove element descriptor token
+            double x1 = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse control point x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            double y1 = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse control point y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            double x = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse target point x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            double y = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse target point y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            x1 = lastPositionX + x1; //compute absolute x coordinate
+            y1 = lastPositionY + y1; //compute absolute y coordinate
+            x = lastPositionX + x; //compute absolute x coordinate
+            y = lastPositionY + y; //compute absolute y coordinate
+            return new Tuple<List<Point>, double, double, double, double>(SampleQuadraticBezier(lastPositionX, lastPositionY, x1, y1, x, y), x, y, x1, y1);
+        }
+
+        /// <summary>
+        /// parses a "quadratic bezier curve shorthand" path element with absolute coordinates
+        /// </summary>
+        /// <param name="pathElements">a list of all not yet parsed path element tokens and values in correct order, starting with the element to be parsed</param>
+        /// <param name="lastPositionX">absolute x coordinate of the last active point</param>
+        /// <param name="lastPositionY">absolute y coordinate of the last active point</param>
+        /// <param name="lastBezierControlPointX">absolute x coordinate of the last bezier control point of the previous bezier curve</param>
+        /// <param name="lastBezierControlPointY">absolute y coordinate of the last bezier control point of the previous bezier curve</param>
+        /// <returns>a List of Points containing all sampled points on the bezier curve, aswell as the unscaled x and y coordinates of the last point of the curve and of the bezier control point</returns>
+        private Tuple<List<Point>, double, double, double, double> Parse_T(List<string> pathElements, double lastPositionX, double lastPositionY, double lastBezierControlPointX, double lastBezierControlPointY)
+        {
+            pathElements.RemoveAt(0); //remove element descriptor token
+            double x = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse target point x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            double y = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse target point y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            double x1 = lastPositionX + (lastPositionX - lastBezierControlPointX); //mirror last bezier control point at bezier start point to get first new bezier control point
+            double y1 = lastPositionY + (lastPositionY - lastBezierControlPointY); //mirror last bezier control point at bezier start point to get first new bezier control point
+            return new Tuple<List<Point>, double, double, double, double>(SampleQuadraticBezier(lastPositionX, lastPositionY, x1, y1, x, y), x, y, x1, y1);
+        }
+
+        /// <summary>
+        /// parses a "quadratic bezier curve shorthand" path element with relative coordinates
+        /// </summary>
+        /// <param name="pathElements">a list of all not yet parsed path element tokens and values in correct order, starting with the element to be parsed</param>
+        /// <param name="lastPositionX">absolute x coordinate of the last active point</param>
+        /// <param name="lastPositionY">absolute y coordinate of the last active point</param>
+        /// <param name="lastBezierControlPointX">absolute x coordinate of the last bezier control point of the previous bezier curve</param>
+        /// <param name="lastBezierControlPointY">absolute y coordinate of the last bezier control point of the previous bezier curve</param>
+        /// <returns>a List of Points containing all sampled points on the bezier curve, aswell as the unscaled x and y coordinates of the last point of the curve and of the bezier control point</returns>
+        private Tuple<List<Point>, double, double, double, double> Parse_t(List<string> pathElements, double lastPositionX, double lastPositionY, double lastBezierControlPointX, double lastBezierControlPointY)
+        {
+            pathElements.RemoveAt(0); //remove element descriptor token
+            double x = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse target point x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            double y = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse target point y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            x = lastPositionX + x; //compute absolute x coordinate
+            y = lastPositionY + y; //compute absolute y coordinate
+            double x1 = lastPositionX + (lastPositionX - lastBezierControlPointX); //mirror last bezier control point at bezier start point to get first new bezier control point
+            double y1 = lastPositionY + (lastPositionY - lastBezierControlPointY); //mirror last bezier control point at bezier start point to get first new bezier control point
+            return new Tuple<List<Point>, double, double, double, double>(SampleQuadraticBezier(lastPositionX, lastPositionY, x1, y1, x, y), x, y, x1, y1);
+        }
+
+        /// <summary>
+        /// parses a "elliptical arc" path element with absolute coordinates
+        /// </summary>
+        /// <param name="pathElements">a list of all not yet parsed path element tokens and values in correct order, starting with the element to be parsed</param>
+        /// <param name="lastPositionX">absolute x coordinate of the last active point</param>
+        /// <param name="lastPositionY">absolute y coordinate of the last active point</param>
+        /// <returns>a List of Points containing all sampled points on the elliptic arc, aswell as the unscaled x and y coordinates of the last point of the arc<returns>
+        private Tuple<List<Point>, double, double> Parse_A(List<string> pathElements, double lastPositionX, double lastPositionY)
+        {
+            pathElements.RemoveAt(0); //remove element descriptor token
+            double rx = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse x radius
+            pathElements.RemoveAt(0); //remove x radius token
+            double ry = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse y radius
+            pathElements.RemoveAt(0); //remove y radius token
+            double thetha = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse rotation
+            pathElements.RemoveAt(0); //remove rotation token
+            bool largeArcFlag = Convert.ToInt16(pathElements.First()) == 1 ? true : false; //parse large arc flag
+            pathElements.RemoveAt(0); //remove large arc flag token
+            bool sweepFlag = Convert.ToInt16(pathElements.First()) == 1 ? true : false; //parse sweep flag
+            pathElements.RemoveAt(0); //remove sweep flag token
+            double x = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse target point x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            double y = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse target point y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            x = x - lastPositionX; //compute relative x coordinate
+            y = y - lastPositionY; //compute relative y coordinate
+            return new Tuple<List<Point>, double, double>(SampleArc(lastPositionX, lastPositionY, rx, ry, x, y, thetha, largeArcFlag, sweepFlag), x, y);
+        }
+
+        /// <summary>
+        /// parses a "elliptical arc" path element with relative coordinates
+        /// </summary>
+        /// <param name="pathElements">a list of all not yet parsed path element tokens and values in correct order, starting with the element to be parsed</param>
+        /// <param name="lastPositionX">absolute x coordinate of the last active point</param>
+        /// <param name="lastPositionY">absolute y coordinate of the last active point</param>
+        /// <returns>a List of Points containing all sampled points on the elliptic arc, aswell as the unscaled x and y coordinates of the last point of the arc</returns>
+        private Tuple<List<Point>, double, double> Parse_a(List<string> pathElements, double lastPositionX, double lastPositionY)
+        {
+            pathElements.RemoveAt(0); //remove element descriptor token
+            double rx = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse x radius
+            pathElements.RemoveAt(0); //remove x radius token
+            double ry = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse y radius
+            pathElements.RemoveAt(0); //remove y radius token
+            double thetha = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse rotation
+            pathElements.RemoveAt(0); //remove rotation token
+            bool largeArcFlag = Convert.ToInt16(pathElements.First()) == 1 ? true : false; //parse large arc flag
+            pathElements.RemoveAt(0); //remove large arc flag token
+            bool sweepFlag = Convert.ToInt16(pathElements.First()) == 1 ? true : false; //parse sweep flag
+            pathElements.RemoveAt(0); //remove sweep flag token
+            double x = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse target point x coordinate
+            pathElements.RemoveAt(0); //remove x coordinate token
+            double y = Convert.ToDouble(pathElements.First(), CultureInfo.InvariantCulture); //parse target point y coordinate
+            pathElements.RemoveAt(0); //remove y coordinate token
+            return new Tuple<List<Point>, double, double>(SampleArc(lastPositionX, lastPositionY, rx, ry, x, y, thetha, largeArcFlag, sweepFlag), x, y);
+        }
+
+        /// <summary>
+        /// samples an arc of an ellipse into a list of points
+        /// </summary>
+        /// <param name="lastPositionX">x coordinate of last point</param>
+        /// <param name="lastPositionY">y coordinate of last point</param>
+        /// <param name="rx">x radius of the ellipse</param>
+        /// <param name="ry">y radius of the ellipse</param>
+        /// <param name="nextPositionXRelative">x coordinate of next point</param>
+        /// <param name="nextPositionYRelative">y coordinate of next point</param>
+        /// <param name="thetha">rotation of the ellipse around the x axis</param>
+        /// <param name="largeArcFlag">flag determining if the large or the small arc is to be drawn</param>
+        /// <param name="sweepFlag">flag determining in which direction the arc is to be drawn (false = ccw, true = cw)</param>
+        /// <returns></returns>
+        private List<Point> SampleArc(double lastPositionX, double lastPositionY, double rx, double ry, double nextPositionXRelative, double nextPositionYRelative, double thetha, bool largeArcFlag, bool sweepFlag)
+        {
+            double cos = Math.Cos(thetha / 180 * Math.PI);
+            double sin = Math.Sin(thetha / 180 * Math.PI);
+            double targetXTransformed = cos * nextPositionXRelative - sin * nextPositionYRelative; //rotate target point counterclockwise around the start point by [thetha] degrees, thereby practically rotating an intermediate coordinate system, which has its origin in the start point, clockwise by the same amount
+            double targetYTransformed = sin * nextPositionXRelative + cos * nextPositionYRelative;
+            var values = SampleEllipticArcBiasedNoRotation(rx, ry, targetXTransformed, targetYTransformed, largeArcFlag, sweepFlag);
+            List<Point> result = new List<Point>();
+            for (int j = 0; j < values.Item1.Length; j++)
+            {
+                double xCoordinateRelative = cos * values.Item1[j] + sin * values.Item2[j]; //rotate backwards so intermediate coordinate system and "real" coordinate system have the same rotation again
+                double yCoordinateRelative = cos * values.Item2[j] - sin * values.Item1[j];
+                double xCoordinateAbsolute = lastPositionX + xCoordinateRelative; //translate relative to absolute coordinates (intermediate coordinate system is now again aligned with the "real" one (the virtual pane on which all vectorgraphic elements are placed) (note that this "real" coordinate system is still not the same as the one actually representing pixels for drawing, as it still has to be scaled appropriately (done inside the ScaleAndCreatePoint method)))
+                double yCoordinateAbsolute = lastPositionY + yCoordinateRelative;
+                result.Add(ScaleAndCreatePoint(xCoordinateAbsolute, yCoordinateAbsolute));
+            }
+            return result;
+        }
+
+        /// <summary>
+        /// samples an elliptical arc with given radii through coordinate origin and endpoint with specified properties
+        /// </summary>
+        /// <param name="rx">x radius</param>
+        /// <param name="ry">y radius</param>
+        /// <param name="targetXTransformed">x coordinate of next point</param>
+        /// <param name="targetYTransformed">y coordinate of next point</param>
+        /// <param name="largeArcFlag">flag determining if the large or the small arc is to be drawn</param>
+        /// <param name="sweepFlag">flag determining in which direction the arc is to be drawn (false = ccw, true = cw)</param>
+        /// <returns></returns>
+        private Tuple<double[], double[]> SampleEllipticArcBiasedNoRotation(double rx, double ry, double targetXTransformed, double targetYTransformed, bool largeArcFlag, bool sweepFlag)
+        {
+            double xStretchFactor = rx / ry; //get rx to ry ratio
+            var values = SampleCircleArcBiasedNoRotation(ry, targetXTransformed / xStretchFactor, targetYTransformed, largeArcFlag, sweepFlag); //get a circular arc with radius ry
+            for (int j = 0; j < values.Item1.Length; j++)
+            {
+                values.Item1[j] = values.Item1[j] * xStretchFactor; //correct x coordinates to get an elliptical arc from a circular one
+            }
+            return values;
+        }
+
+        /// <summary>
+        /// samples a circular arc with given radius through coordinate origin and endpoint with specified properties
+        /// </summary>
+        /// <param name="r">radius</param>
+        /// <param name="nextPositionXRelative">x coordinate of next point</param>
+        /// <param name="nextPositionYRelative">y coordinate of next point</param>
+        /// <param name="largeArcFlag">flag determining if the large or the small arc is to be drawn</param>
+        /// <param name="sweepFlag">flag determining in which direction the arc is to be drawn (false = ccw, true = cw)</param>
+        /// <returns></returns>
+        private Tuple<double[], double[]> SampleCircleArcBiasedNoRotation(double r, double nextPositionXRelative, double nextPositionYRelative, bool largeArcFlag, bool sweepFlag)
+        {
+            // code for center computation adapted from https://stackoverflow.com/a/36211852
+            double radsq = r * r;
+            double q = Math.Sqrt(((nextPositionXRelative) * (nextPositionXRelative)) + ((nextPositionYRelative) * (nextPositionYRelative))); //Math.Sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));
+            double x3 = (nextPositionXRelative) / 2; //(x1 + x2) / 2;
+            double y3 = (nextPositionYRelative) / 2; //(y1 + y2) / 2;
+            bool xPlusFlag; //flags needed to select center point "left" of the line between origin and the endpoint (will be used to select correct one ("left" or "right" one) later together with flags passed as arguments
+            bool yPlusFlag;
+            if (nextPositionXRelative > 0)
+            {
+                yPlusFlag = true; //left point lies above line
+            }
+            else
+            {
+                yPlusFlag = false; //left point lies below line
+            }
+            if (nextPositionYRelative > 0)
+            {
+                xPlusFlag = false; //left point lies left of line
+            }
+            else
+            {
+                xPlusFlag = true; //left point lies right of line
+            }
+            if(sweepFlag != largeArcFlag) //need "right" center point, not "left" one (refer to svg specification, sweepFlag means going around the circle in "clockwise" direction, largeArcFlag means tracing the larger of the two possible arcs in the selected direction) 
+            {
+                xPlusFlag = !xPlusFlag;
+                yPlusFlag = !yPlusFlag;
+            }
+            double xC; // coordinates of center point of circle
+            double yC;
+            if(xPlusFlag) xC = x3 + Math.Sqrt(radsq - ((q / 2) * (q / 2))) * ((nextPositionYRelative) / q); //x3 + Math.Sqrt(radsq - ((q / 2) * (q / 2))) * ((y1 - y2) / q);
+            else xC = x3 - Math.Sqrt(radsq - ((q / 2) * (q / 2))) * ((nextPositionYRelative) / q);
+            if (yPlusFlag) yC = y3 + Math.Sqrt(radsq - ((q / 2) * (q / 2))) * ((nextPositionXRelative) / q); //y3 + Math.Sqrt(radsq - ((q / 2) * (q / 2))) * ((x2-x1) / q);
+            else yC = y3 - Math.Sqrt(radsq - ((q / 2) * (q / 2))) * ((nextPositionXRelative) / q);
+            var values = SampleCircleArcBiasedAroundCenter(-xC, -yC, nextPositionXRelative - xC, nextPositionYRelative - yC, r, largeArcFlag, sweepFlag);
+            for (int j = 0; j < values.Item1.Length; j++)
+            {
+                values.Item1[j] = values.Item1[j] + xC; //correct center point coordinate bias
+                values.Item2[j] = values.Item2[j] + yC; 
+            }
+            return values;
+        }
+
+        /// <summary>
+        /// samples a circular arc with given radius around the center from the startpoint to the endpoint in the specified direction
+        /// </summary>
+        /// <param name="xStartPoint">x coordinate of the start point</param>
+        /// <param name="yStartPoint">y coordinate of the start point</param>
+        /// <param name="xFinalPoint">x coordinate of the final point</param>
+        /// <param name="yFinalPoint">y coordinate of the final point</param>
+        /// <param name="r">radius</param>
+        /// <param name="clockwise">direction</param>
+        /// <returns></returns>
+        private Tuple<double[], double[]> SampleCircleArcBiasedAroundCenter(double xStartPoint, double yStartPoint, double xFinalPoint, double yFinalPoint, double r, bool largeArcFlag, bool clockwise)
+        {
+            double phiEnd = Math.Atan2(yFinalPoint, xFinalPoint); // angles between points and origin and the positive x Axis
+            double phiStart = Math.Atan2(yStartPoint, xStartPoint);
+            double angle = ((double)2 * Math.PI) / (double)samplingRateEllipse; //compute angle increment (equal to the one used for ellipses)
+            double angleDifference = Math.Abs(phiStart - phiEnd);
+            if (angleDifference > 2 * Math.PI || angleDifference < 0) throw new Exception("angleDifference out of range: " + angleDifference); //TODO remove
+            if (largeArcFlag) // get larger angleDifference
+            {
+                if (angleDifference < Math.PI) angleDifference = ((double)2 * Math.PI) - angleDifference;  // was smaller angleDifference
+            }
+            else // get smaller angleDifference
+            {
+                if(angleDifference > Math.PI) angleDifference = ((double)2 * Math.PI) - angleDifference;  // was larger angleDifference
+            }
+            int numberOfPoints = (int) Math.Ceiling(angleDifference / angle);  //compute number of points to sample
+            double[] xValues = new double[numberOfPoints];
+            double[] yValues = new double[numberOfPoints];
+            double phiCurrent = phiStart;
+            for (int j = 0; j < numberOfPoints-1; j++) //compute intermediate points
+            {
+                if (clockwise) phiCurrent -= angle; //get new angle
+                else phiCurrent += angle;
+                yValues[j] = Math.Sin(phiCurrent) * r; //angles are relative to positive x Axis!
+                xValues[j] = Math.Cos(phiCurrent) * r;
+            }
+            xValues[numberOfPoints - 1] = xFinalPoint; //(last segment always has an angle of less than or exactly 'angle')
+            yValues[numberOfPoints - 1] = yFinalPoint;
+            return new Tuple<double[], double[]>(xValues, yValues);
+        }
+
+        /// <summary>
+        /// samples a cubic bezier curve with a static number of steps (samplingRateBezier)
+        /// </summary>
+        /// <param name="lastPositionX">x coordinate of last point</param>
+        /// <param name="lastPositionY">y coordinate of last point</param>
+        /// <param name="controlPoint1X">x coordinate of control point 1</param>
+        /// <param name="controlPoint1Y">y coordinate of control point 1</param>
+        /// <param name="controlPoint2X">x coordinate of control point 2</param>
+        /// <param name="controlPoint2Y">y coordinate of control point 2</param>
+        /// <param name="nextPositionX">x coordinate of next point</param>
+        /// <param name="nextPositionY">y coordinate of next point</param>
+        /// <returns>a List of Points containing all sampled points</returns>
+        private List<Point> SampleCubicBezier(double lastPositionX, double lastPositionY, double controlPoint1X, double controlPoint1Y, double controlPoint2X, double controlPoint2Y, double nextPositionX, double nextPositionY)
+        {
+            var line1 = CreateDiscreteLine(lastPositionX, lastPositionY, controlPoint1X, controlPoint1Y);
+            var line2 = CreateDiscreteLine(controlPoint1X, controlPoint1Y, controlPoint2X, controlPoint2Y);
+            var line3 = CreateDiscreteLine(controlPoint2X, controlPoint2Y, nextPositionX, nextPositionY);
+            var quadraticBezier1 = ComputeBezierStep(line1.Item1, line1.Item2, line2.Item1, line2.Item2);
+            var quadraticBezier2 = ComputeBezierStep(line2.Item1, line2.Item2, line3.Item1, line3.Item2);
+            var values = ComputeBezierStep(quadraticBezier1.Item1, quadraticBezier1.Item2, quadraticBezier2.Item1, quadraticBezier2.Item2);
+            List<Point> result = new List<Point>();
+            for (int j = 0; j < samplingRateBezier; j++)
+            {
+                result.Add(ScaleAndCreatePoint(values.Item1[j], values.Item2[j]));
+            }
+            return result;
+        }
+
+        /// <summary>
+        /// samples a quadratic bezier curve with a static number of steps (samplingRateBezier)
+        /// </summary>
+        /// <param name="lastPositionX">x coordinate of last point</param>
+        /// <param name="lastPositionY">y coordinate of last point</param>
+        /// <param name="controlPointX">x coordinate of control point</param>
+        /// <param name="controlPointY">y coordinate of control point</param>
+        /// <param name="nextPositionX">x coordinate of next point</param>
+        /// <param name="nextPositionY">y coordinate of next point</param>
+        /// <returns>a List of Points containing all sampled points</returns>
+        private List<Point> SampleQuadraticBezier(double lastPositionX, double lastPositionY, double controlPointX, double controlPointY, double nextPositionX, double nextPositionY)
+        {
+            var line1 = CreateDiscreteLine(lastPositionX, lastPositionY, controlPointX, controlPointY);
+            var line2 = CreateDiscreteLine(controlPointX, controlPointY, nextPositionX, nextPositionY);
+            var values = ComputeBezierStep(line1.Item1, line1.Item2, line2.Item1, line2.Item2);
+            List<Point> result = new List<Point>();
+            for (int j = 0; j < samplingRateBezier; j++)
+            {
+                result.Add(ScaleAndCreatePoint(values.Item1[j], values.Item2[j]));
+            }
+            return result;
+        }
+
+        /// <summary>
+        /// create a discrete line with [samplingRateBezier] points (including start and end point) between two points
+        /// </summary>
+        /// <param name="point1X">coordinate of point 1</param>
+        /// <param name="point1Y">y coordinate of point 1</param>
+        /// <param name="point2X">x coordinate of point 2</param>
+        /// <param name="point2Y">y coordinate of point 2</param>
+        /// <returns>the discrete line as arrays of x and y coordinates</returns>
+        private Tuple<double[], double[]> CreateDiscreteLine(double point1X, double point1Y, double point2X, double point2Y)
+        {
+            double[] resultX = new double[samplingRateBezier];
+            double[] resultY = new double[samplingRateBezier];
+            for (int j = 0; j < samplingRateBezier; j++)
+            {
+                var pointResult = LinearInterpolationForBezier(point1X, point1Y, point2X, point2Y, j);
+                resultX[j] = pointResult.Item1;
+                resultY[j] = pointResult.Item2;
+            }
+            return new Tuple<double[], double[]>(resultX, resultY);
+        }
+
+        /// <summary>
+        /// computes the discrete bezier curve between two given dicrete lines/curves
+        /// </summary>
+        /// <param name="line1X">x coordinates of all points in line 1</param>
+        /// <param name="line1Y">y coordinates of all points in line 1</param>
+        /// <param name="line2X">x coordinates of all points in line 2</param>
+        /// <param name="line2Y">y coordinates of all points in line 2</param>
+        /// <returns>the discrete bezier curve</returns>
+        private Tuple<double[], double[]> ComputeBezierStep(double[] line1X, double[] line1Y, double[] line2X, double[] line2Y)
+        {
+            double[] resultX = new double[samplingRateBezier];
+            double[] resultY = new double[samplingRateBezier];
+            for (int j = 0; j < samplingRateBezier; j++)
+            {
+                var pointResult = LinearInterpolationForBezier(line1X[j], line1Y[j], line2X[j], line2Y[j], j);
+                resultX[j] = pointResult.Item1;
+                resultY[j] = pointResult.Item2;
+            }
+            return new Tuple<double[], double[]>(resultX, resultY);
+        }
+
+        /// <summary>
+        /// creates the linearly interpolated point at j/(samplingRateBezier - 1) between point 1 and point 2
+        /// </summary>
+        /// <param name="point1X">x coordinate of point 1</param>
+        /// <param name="point1Y">y coordinate of point 1</param>
+        /// <param name="point2X">x coordinate of point 2</param>
+        /// <param name="point2Y">y coordinate of point 2</param>
+        /// <param name="j">number of point to be interpolated, at a total number of [samplingRateBezier] points</param>
+        /// <returns>the linearly interpolated point</returns>
+        private Tuple<double, double> LinearInterpolationForBezier(double point1X, double point1Y, double point2X, double point2Y, int j)
+        {
+            double factor = ((double)1 / (double)(samplingRateBezier - 1)) * (double)j; //factor for linear interpolation
+            double x = point1X + ((point2X - point1X) * factor);
+            double y = point1Y + ((point2Y - point1Y) * factor);
+            return new Tuple<double, double>(x, y);
+        }
+
+        /// <summary>
+        /// parses a hierarchical svg element and all its sub-elements
+        /// </summary>
+        /// <param name="currentElement">the definition of the top level element as whitespace seperated String[]</param>
+        /// <param name="allLines">an array holding all lines of the input file</param>
+        /// <returns>the parsed element as a Line object, or null if the element is not supported</returns>
+        private List<InternalLine> ParseMultiLineSVGElement(string[] currentElement, string[] allLines)
+        {
+            throw new NotImplementedException();
+        }
+
+        /// <summary>
+        /// removes the name of the attribute aswell as the '="' at the beginning and the '"' or '">' at the end of an attribute definition
+        /// </summary>
+        /// <param name="definition">the definition from the svg file</param>
+        /// <returns>the value of the attribute, as String (the part of definition contained between '"'s)</returns>
+        private String ParseSingleSVGAttribute(String definition)
+        {
+            return definition.Split('"')[1];
+        }
+
+        /// <summary>
+        /// fetches a single svg element definition that may extend ovr several lines of the input file, iterates i to point to the last line of the element definition
+        /// </summary>
+        /// <param name="allLines">an array holding all lines of the input file</param>
+        /// <returns>the definition of the current svg element, as String[] split by whitespaces</returns>
+        private String[] GetCurrentElement(String[] allLines)
+        {
+            List<String> currentElementTemp = allLines[i].Split(whitespaces).ToList();
+            while (!currentElementTemp.Last().EndsWith(">"))
+            {
+                i++;
+                currentElementTemp.AddRange(allLines[i].Split(whitespaces).ToList());
+            }
+            return currentElementTemp.ToArray();
+        }
+
+        /// <summary>
+        /// applies the scale factor to the coordinates and creates a new Point
+        /// </summary>
+        /// <param name="x">unscaled x coordinate</param>
+        /// <param name="y">unscaled y coordinate</param>
+        /// <returns>new Point with scaled coordinates</returns>
+        private Point ScaleAndCreatePoint(double x, double y)
+        {
+            return new Point((int)Math.Round(x * scale), (int)Math.Round(y * scale));
+        }
+
+        /// <summary>
+        /// creates a representation of an ellipse as a List of Points by sampling the outline of the ellipse
+        /// </summary>
+        /// <param name="x">x coordinate of the center of the ellipse</param>
+        /// <param name="y">y coordinate of the center of the ellipse</param>
+        /// <param name="rx">x radius of the ellipse</param>
+        /// <param name="ry">y radius of the ellipse</param>
+        /// <returns>the parsed element as a List of Points</returns>
+        private List<Point> SampleEllipse(double x, double y, double rx, double ry)
+        {
+            List<Point> ellipse = new List<Point>();
+            double angle = ((double)2 * Math.PI) / (double)samplingRateEllipse;
+            double yScale = ry / rx;
+            double[] xValues = new double[samplingRateEllipse / 4];
+            double[] yValues = new double[samplingRateEllipse / 4];
+            for (int j = 0; j < samplingRateEllipse / 4; j++) //compute offset values of points for one quadrant
+            {
+                xValues[j] = Math.Sin((double)j * angle) * rx;
+                yValues[j] = Math.Cos((double)j * angle) * rx;
+            }
+            for (int j = 0; j < samplingRateEllipse / 4; j++) //create actual points for first quadrant
+            {
+                int xCoord = Convert.ToInt32(Math.Round(x + xValues[j]));
+                int yCoord = Convert.ToInt32(Math.Round(y - yValues[j] * yScale));
+                ellipse.Add(ScaleAndCreatePoint(xCoord, yCoord));
+            }
+            for (int j = 0; j < samplingRateEllipse / 4; j++) //create actual points for second quadrant
+            {
+                int xCoord = Convert.ToInt32(Math.Round(x + yValues[j]));
+                int yCoord = Convert.ToInt32(Math.Round(y + xValues[j] * yScale));
+                ellipse.Add(ScaleAndCreatePoint(xCoord, yCoord));
+            }
+            for (int j = 0; j < samplingRateEllipse / 4; j++) //create actual points for third quadrant
+            {
+                int xCoord = Convert.ToInt32(Math.Round(x - xValues[j]));
+                int yCoord = Convert.ToInt32(Math.Round(y + yValues[j] * yScale));
+                ellipse.Add(ScaleAndCreatePoint(xCoord, yCoord));
+            }
+            for (int j = 0; j < samplingRateEllipse / 4; j++) //create actual points for fourth quadrant
+            {
+                int xCoord = Convert.ToInt32(Math.Round(x - yValues[j]));
+                int yCoord = Convert.ToInt32(Math.Round(y - xValues[j] * yScale));
+                ellipse.Add(ScaleAndCreatePoint(xCoord, yCoord));
+            }
+            ellipse.Add(ScaleAndCreatePoint(Convert.ToInt32(Math.Round(x + 0)), Convert.ToInt32(Math.Round(y - rx * yScale)))); //close ellipse
+            return ellipse;
+        }
+    }
+}

+ 3 - 3
SketchAssistant/SketchAssistant/FileImporterException.cs → SketchAssistant/SketchAssistantWPF/FileImporterException.cs

@@ -4,7 +4,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace SketchAssistant
+namespace SketchAssistantWPF
 {
     public class FileImporterException : Exception
     {
@@ -13,9 +13,9 @@ namespace SketchAssistant
         /// </summary>
         String showMessage;
 
-        public FileImporterException(String message, String hint, int lineNumber) : base (message)
+        public FileImporterException(String message, String hint, int lineNumber) : base(message)
         {
-            showMessage = "Could not import file:\n\n" + message + (hint == null ? "" : "\n(Hint: " + hint + ")") + (lineNumber == -1 ? "" : "\n\n-line: " + lineNumber );
+            showMessage = "Could not import file:\n\n" + message + (hint == null ? "" : "\n(Hint: " + hint + ")") + (lineNumber == -1 ? "" : "\n\n-line: " + lineNumber);
         }
 
         public override string ToString()

+ 305 - 0
SketchAssistant/SketchAssistantWPF/GeometryCalculator.cs

@@ -0,0 +1,305 @@
+using System;
+using System.Collections.Generic;
+using System.Windows;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SketchAssistantWPF
+{
+    /// <summary>
+    /// A class that contains all algorithms related to geometry.
+    /// </summary>
+    public static class GeometryCalculator
+    {
+        /// <summary>
+        /// Calculate the approximate similarity of two lines. 
+        /// Using three weighted parameters to calculate a value between 0 and 1 to indicate the similarity of the lines.
+        /// </summary>
+        /// <param name="line0">The first line.</param>
+        /// <param name="line1">The second line.</param>
+        /// <returns>The similarity of the two lines.</returns>
+        public static double CalculateSimilarity(InternalLine line0, InternalLine line1)
+        {
+            double CosSim = Math.Abs(CalculateAverageCosineSimilarity(line0, line1));
+            double LenSim = CalculateLengthSimilarity(line0, line1);
+            double AvDist = CalculateAverageDistance(line0, line1);
+            double DistSim = (50 - AvDist) / 50;
+            if (DistSim < 0) DistSim = 0;
+            if (CosSim < 0.5 || Double.IsNaN(CosSim)) CosSim = 0;
+            double output = (2 * CosSim + LenSim + DistSim) / 4;
+            System.Diagnostics.Debug.WriteLine("Results: CosSim: {0}, LenSim: {1}, AvDist {2}, DistSim: {3}. Total: {4}", 
+                CosSim, LenSim, AvDist, DistSim, output);
+            return output;
+        }
+
+        /// <summary>
+        /// The cosine similarity of two vectors.
+        /// </summary>
+        /// <param name="v0">The first vector</param>
+        /// <param name="v1">The second vector</param>
+        /// <returns>The cosine similarity</returns>
+        private static double CosineSimilarity(Vector v0, Vector v1)
+        {
+            return (v0.X * v1.X + v0.Y * v1.Y) / (Math.Sqrt(v0.X * v0.X + v0.Y * v0.Y) * Math.Sqrt(v1.X * v1.X + v1.Y * v1.Y));
+        }
+
+        /// <summary>
+        /// An approximate calculation of the average cosine similarity 
+        /// of two lines, achieved by calculating the cosine similarity of subvectors.
+        /// </summary>
+        /// <param name="line0">The first line</param>
+        /// <param name="line1">The second line</param>
+        /// <returns>The approximate average cosine similarity of all subvectors</returns>
+        public static double CalculateAverageCosineSimilarity(InternalLine line0, InternalLine line1)
+        {
+            //check if one of the lines is a point, or both lines are points
+            if ((line0.isPoint && !line1.isPoint) || (line1.isPoint && !line0.isPoint)) return 0;
+            else if (line0.isPoint && line1.isPoint) return 1;
+
+            List<Point> points0 = line0.GetPoints();
+            List<Point> points1 = line1.GetPoints();
+
+            if (points0.Count == points1.Count)
+            {
+                //If the two lists have an equal amount of subvectors, 
+                //compare the nth subvectors from each list and average the value.
+                double sum = 0; int i = 0;
+                List<Point> shortL = points0; List<Point> longL = points1;
+                for (; i < shortL.Count - 1; i++)
+                {
+                    if (i + 1 == shortL.Count || i + 1 == longL.Count) break;
+                    Vector v0 = new Vector(shortL[i + 1].X - shortL[i].X, shortL[i + 1].Y - shortL[i].Y);
+                    Vector v1 = new Vector(longL[i + 1].X - longL[i].X, longL[i + 1].Y - longL[i].Y);
+                    sum += CosineSimilarity(v0, v1);
+                }
+                return sum / i;
+            }
+            else
+            {
+                //Determine if the longer list is of similar length or contains significatly more items
+                List<Point> shortL = points0; List<Point> longL = points0;
+                if (points0.Count < points1.Count) { longL = points1; }
+                if (points0.Count > points1.Count) { shortL = points1;}
+                double dif = (longL.Count - 1) / (shortL.Count - 1);
+                if(dif > 1)
+                {
+                    //The longer list is significantly longer
+                    //Each element in the shorter list is compared to multiple 
+                    // elements in the longer list to make up the difference
+                    double sum = 0; int adds = 0;
+
+                    for (int i = 0; i < shortL.Count - 1; i++)
+                    {
+                        if (i + 1 == shortL.Count) break;
+                        for (int j = 0; j <= dif; j++)
+                        {
+                            var k = i + j;
+                            if (k + 1 == longL.Count) break;
+                            Vector v0 = new Vector(shortL[i + 1].X - shortL[i].X, shortL[i + 1].Y - shortL[i].Y);
+                            Vector v1 = new Vector(longL[k + 1].X - longL[k].X, longL[k + 1].Y - longL[k].Y);
+                            sum += CosineSimilarity(v0, v1); adds++;
+                        }
+                    }
+                    return sum / adds;
+                }
+                else
+                {
+                    //The longer list is almost the same length as the shorter list
+                    //The remaining items are simply skipped
+                    double sum = 0; int i = 0;
+                    for (; i < shortL.Count - 1; i++)
+                    {
+                        if (i + 1 == shortL.Count || i + 1 == longL.Count) break;
+                        Vector v0 = new Vector(shortL[i + 1].X - shortL[i].X, shortL[i + 1].Y - shortL[i].Y);
+                        Vector v1 = new Vector(longL[i + 1].X - longL[i].X, longL[i + 1].Y - longL[i].Y);
+                        sum += CosineSimilarity(v0, v1);
+                    }
+                    return sum / i;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Calculate the similarity in length of two Lines.
+        /// </summary>
+        /// <param name="line0">The first line.</param>
+        /// <param name="line1">The second line.</param>
+        /// <returns>How similar the lines are in length.</returns>
+        private static double CalculateLengthSimilarity(InternalLine line0, InternalLine line1)
+        {
+            double len0 = line0.GetLength(); double len1 = line1.GetLength();
+            var dif = Math.Abs(len1 - len0);
+            if (dif == 0) return 1;
+            double shorter;
+            if (len1 > len0) shorter = len0;
+            else shorter = len1;
+            if (dif >= shorter) return 0;
+            return (shorter - dif )/shorter;
+        }
+
+        /// <summary>
+        /// Calculate the average distance between the ends of two lines.
+        /// </summary>
+        /// <param name="line0">The first line.</param>
+        /// <param name="line1">The second line.</param>
+        /// <returns>The shortest average distance between the ends of the lines.</returns>
+        private static double CalculateAverageDistance(InternalLine line0, InternalLine line1)
+        {
+            List<Point> points0 = line0.GetPoints();
+            List<Point> points1 = line1.GetPoints();
+            double distfirstfirst = Math.Sqrt(Math.Pow((points0[0].X - points1[0].X) , 2) + Math.Pow((points0[0].Y - points1[0].Y) , 2));
+            double distlastlast = Math.Sqrt(Math.Pow((points0.Last().X - points1.Last().X), 2) + Math.Pow((points0.Last().Y - points1.Last().Y), 2));
+            double distfirstlast = Math.Sqrt(Math.Pow((points0[0].X - points1.Last().X), 2) + Math.Pow((points0[0].Y - points1.Last().Y), 2));
+            double distlastfirst = Math.Sqrt(Math.Pow((points0.Last().X - points1[0].X), 2) + Math.Pow((points0.Last().Y - points1[0].Y), 2));
+            if ((distfirstfirst + distlastlast) / 2 < (distfirstlast + distlastfirst) / 2) return (distfirstfirst + distlastlast) / 2;
+            else return (distfirstlast + distlastfirst) / 2;
+        }
+
+        /// <summary>
+        /// A simple algorithm that returns a filled circle with a radius and a center point.
+        /// </summary>
+        /// <param name="center">The center point of the alorithm </param>
+        /// <param name="radius">The radius of the circle, if its less or equal to 1 
+        /// only the center point is returned. </param>
+        /// <returns>All the points in or on the circle.</returns>
+        public static HashSet<Point> FilledCircleAlgorithm(Point center, int radius)
+        {
+            HashSet<Point> returnSet = new HashSet<Point> { center };
+            //Fill the circle
+            for (int x = 0; x < radius; x++)
+            {
+                for (int y = 0; y < radius; y++)
+                {
+                    //Check if point is on or in the circle
+                    if ((x * x + y * y - radius * radius) <= 0)
+                    {
+                        returnSet.Add(new Point(center.X + x, center.Y + y));
+                        returnSet.Add(new Point(center.X - x, center.Y + y));
+                        returnSet.Add(new Point(center.X + x, center.Y - y));
+                        returnSet.Add(new Point(center.X - x, center.Y - y));
+                    }
+                }
+            }
+            return returnSet;
+        }
+
+        /// <summary>
+        /// An implementation of the Bresenham Line Algorithm, 
+        /// which calculates all points between two points in a straight line.
+        /// Implemented using the pseudocode on Wikipedia.
+        /// </summary>
+        /// <param name="p0">The start point</param>
+        /// <param name="p1">The end point</param>
+        /// <returns>All points between p0 and p1 (including p0 and p1)</returns>
+        public static List<Point> BresenhamLineAlgorithm(Point p0, Point p1)
+        {
+            int p1x = (int)p1.X;
+            int p1y = (int)p1.Y;
+            int p0x = (int)p0.X;
+            int p0y = (int)p0.Y;
+
+            int deltaX = p1x - p0x;
+            int deltaY = p1y - p0y;
+            List<Point> returnList;
+
+            if (Math.Abs(deltaY) < Math.Abs(deltaX))
+            {
+                if (p0.X > p1.X)
+                {
+                    returnList = GetLineLow(p1x, p1y, p0x, p0y);
+                    returnList.Reverse();
+                }
+                else
+                {
+                    returnList = GetLineLow(p0x, p0y, p1x, p1y);
+                }
+            }
+            else
+            {
+                if (p0.Y > p1.Y)
+                {
+                    returnList = GetLineHigh(p1x, p1y, p0x, p0y);
+                    returnList.Reverse();
+                }
+                else
+                {
+                    returnList = GetLineHigh(p0x, p0y, p1x, p1y);
+                }
+            }
+            return returnList;
+        }
+
+        /// <summary>
+        /// Helping function of the Bresenham Line algorithm,
+        /// under the assumption that abs(deltaY) is smaller than abs(deltX)
+        /// and x0 is smaller than x1
+        /// </summary>
+        /// <param name="x0">x value of point 0</param>
+        /// <param name="y0">y value of point 0</param>
+        /// <param name="x1">x value of point 1</param>
+        /// <param name="y1">y value of point 1</param>
+        /// <returns>All points on the line between the two points</returns>
+        private static List<Point> GetLineLow(int x0, int y0, int x1, int y1)
+        {
+            List<Point> returnList = new List<Point>();
+            int dx = x1 - x0;
+            int dy = y1 - y0;
+            int yi = 1;
+            if (dy < 0)
+            {
+                yi = -1;
+                dy = -dy;
+            }
+            int D = 2 * dy - dx;
+            int y = y0;
+            for (int x = x0; x <= x1; x++)
+            {
+                returnList.Add(new Point(x, y));
+                if (D > 0)
+                {
+                    y = y + yi;
+                    D = D - 2 * dx;
+                }
+                D = D + 2 * dy;
+            }
+            return returnList;
+        }
+
+        /// <summary>
+        /// Helping function of the Bresenham Line algorithm,
+        /// under the assumption that abs(deltaY) is larger or equal than abs(deltX)
+        /// and y0 is smaller than y1
+        /// </summary>
+        /// <param name="x0">x value of point 0</param>
+        /// <param name="y0">y value of point 0</param>
+        /// <param name="x1">x value of point 1</param>
+        /// <param name="y1">y value of point 1</param>
+        /// <returns>All points on the line between the two points</returns>
+        private static List<Point> GetLineHigh(int x0, int y0, int x1, int y1)
+        {
+            List<Point> returnList = new List<Point>();
+            int dx = x1 - x0;
+            int dy = y1 - y0;
+            int xi = 1;
+            if (dx < 0)
+            {
+                xi = -1;
+                dx = -dx;
+            }
+            int D = 2 * dx - dy;
+            int x = x0;
+            for (int y = y0; y <= y1; y++)
+            {
+                returnList.Add(new Point(x, y));
+                if (D > 0)
+                {
+                    x = x + xi;
+                    D = D - 2 * dy;
+                }
+                D = D + 2 * dx;
+            }
+            return returnList;
+        }
+    }
+}

+ 27 - 0
SketchAssistant/SketchAssistantWPF/ImageDimension.cs

@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SketchAssistantWPF
+{
+    public class ImageDimension
+    {
+        public int Width { get; set; }
+
+        public int Height { get; set; }
+
+        public ImageDimension(int width, int height)
+        {
+            Width = width;
+            Height = height;
+        }
+
+        public void ChangeDimension(int width, int height)
+        {
+            Width = width;
+            Height = height;
+        }
+    }
+}

+ 194 - 0
SketchAssistant/SketchAssistantWPF/InternalLine.cs

@@ -0,0 +1,194 @@
+using System;
+using System.Collections.Generic;
+using System.Windows;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Media;
+
+namespace SketchAssistantWPF
+{
+    public class InternalLine
+    {
+        /// <summary>
+        /// list saving all the points of the line in the order of the path from start to end point
+        /// </summary>
+        private List<Point> linePoints;
+        /// <summary>
+        /// unique identifier of this Line object
+        /// </summary>
+        private int identifier;
+        /// <summary>
+        /// flag showing if this is only a temporary line
+        /// </summary>
+        private bool isTemporary;
+        /// <summary>
+        /// A collection of the original Points defining the line.
+        /// </summary>
+        private PointCollection pointColl;
+        /// <summary>
+        /// Indicates if this is a single point.
+        /// </summary>
+        public bool isPoint { get; private set; }
+        /// <summary>
+        /// The location of the point, if this is a point
+        /// </summary>
+        public Point point { get; private set; }
+        /// <summary>
+        /// The length of the line
+        /// </summary>
+        private double length = -1;
+
+        /// <summary>
+        /// The constructor for lines which are only temporary.
+        /// If you want nice lines use the other constructor.
+        /// </summary>
+        /// <param name="points">The points of the line</param>
+        public InternalLine(List<Point> points)
+        {
+            linePoints = new List<Point>(points);
+            pointColl = new PointCollection(linePoints);
+            isTemporary = true;
+        }
+
+        /// <summary>
+        /// The constructor for lines, which will be more resource efficient 
+        /// and have the ability to populate deletion matrixes.
+        /// </summary>
+        /// <param name="points">The points of the line</param>
+        /// <param name="id">The identifier of the line</param>
+        public InternalLine(List<Point> points, int id)
+        {
+            linePoints = new List<Point>(points);
+            pointColl = new PointCollection(linePoints);
+            identifier = id;
+            CleanPoints();
+            isTemporary = false;
+        }
+
+        /// <summary>
+        /// A function to make temporary lines non-temporary.
+        /// </summary>
+        /// <param name="id">The id of the line.</param>
+        public void MakePermanent(int id)
+        {
+            if (isTemporary)
+            {
+                identifier = id;
+                CleanPoints();
+                isTemporary = false;
+            }
+        }
+
+        public List<Point> GetPoints()
+        {
+            return linePoints;
+        }
+
+        public int GetID()
+        {
+            return identifier;
+        }
+
+        public PointCollection GetPointCollection()
+        {
+            return pointColl;
+        }
+
+
+        /// <summary>
+        /// Get the length of the line.
+        /// </summary>
+        /// <returns>The length of the line.</returns>
+        public double GetLength()
+        {
+            if(length < 0)
+            {
+                length = 0;
+                for(int i = 0; i < linePoints.Count - 1; i++)
+                {
+                    var a = linePoints[i]; var b = linePoints[i + 1];
+
+                    length += Math.Sqrt(Math.Pow((a.X - b.X),2) + Math.Pow((a.Y - b.Y), 2));
+                }
+            }
+            return length;
+        }
+
+        /// <summary>
+        /// A function that will take two matrixes and populate them with the line data of this line object
+        /// </summary>
+        /// <param name="boolMatrix">The Matrix of booleans, in which is saved wether there is a line at this position.</param>
+        /// <param name="listMatrix">The Matrix of Lists of integers, in which is saved which lines are at this position</param>
+        public void PopulateMatrixes(bool[,] boolMatrix, HashSet<int>[,] listMatrix)
+        {
+            if (!isTemporary)
+            {
+                foreach (Point currPoint in linePoints)
+                {
+                    if (currPoint.X >= 0 && currPoint.Y >= 0 &&
+                        currPoint.X < boolMatrix.GetLength(0) && currPoint.Y < boolMatrix.GetLength(1))
+                    {
+                        boolMatrix[(int) currPoint.X, (int) currPoint.Y] = true;
+                        if (listMatrix[(int) currPoint.X, (int) currPoint.Y] == null)
+                        {
+                            listMatrix[(int) currPoint.X, (int) currPoint.Y] = new HashSet<int>();
+                        }
+                        listMatrix[(int) currPoint.X, (int) currPoint.Y].Add(identifier);
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Removes duplicate points from the line object
+        /// </summary>
+        private void CleanPoints()
+        {
+            if (linePoints.Any())
+            {
+                //check if its a point
+                var localIsPoint = linePoints.All(o => o.X == linePoints.First().X && o.Y == linePoints.First().Y);
+                if (!localIsPoint) {
+                    List<Point> newList = new List<Point>();
+                    List<Point> tempList = new List<Point>();
+                    //Since Point is non-nullable, we must ensure the nullPoints, 
+                    //which we remove can not possibly be points of the original given line.
+                    int nullValue = (int) linePoints[0].X + 1;
+                    //Fill the gaps between points
+                    for (int i = 0; i < linePoints.Count - 1; i++)
+                    {
+                        nullValue += (int) linePoints[i + 1].X;
+                        List<Point> partialList = GeometryCalculator.BresenhamLineAlgorithm(linePoints[i], linePoints[i + 1]);
+                        tempList.AddRange(partialList);
+                    }
+                    Point nullPoint = new Point(nullValue, 0);
+                    //Set duplicate points to the null point
+                    for (int i = 1; i < tempList.Count; i++)
+                    {
+                        if ((tempList[i].X == tempList[i - 1].X) && (tempList[i].Y == tempList[i - 1].Y))
+                        {
+                            tempList[i - 1] = nullPoint;
+                        }
+                    }
+                    //remove the null points
+                    foreach (Point tempPoint in tempList)
+                    {
+                        if (tempPoint.X != nullValue)
+                        {
+                            newList.Add(tempPoint);
+                        }
+                    }
+                    linePoints = new List<Point>(newList);
+                }
+                else
+                {
+                    isPoint = true;
+                    point = linePoints.First();
+                    linePoints.Clear();
+                    linePoints.Add(point);
+                }
+            }
+        }
+    }
+}

+ 422 - 0
SketchAssistant/SketchAssistantWPF/MVP_Model.cs

@@ -0,0 +1,422 @@
+using System;
+using System.Collections.Generic;
+using System.Windows;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace SketchAssistantWPF
+{
+    public class MVP_Model
+    {
+        /// <summary>
+        /// The Presenter of the MVP-Model.
+        /// </summary>
+        MVP_Presenter programPresenter;
+        /// <summary>
+        /// History of Actions
+        /// </summary>
+        ActionHistory historyOfActions;
+        /// <summary>
+        /// The assistant responsible for the redraw mode
+        /// </summary>
+        //RedrawAssistant redrawAss;
+
+
+        /***********************/
+        /*** CLASS VARIABLES ***/
+        /***********************/
+
+        /// <summary>
+        /// If the program is in drawing mode.
+        /// </summary>
+        bool inDrawingMode;
+        /// <summary>
+        /// Size of deletion area
+        /// </summary>
+        int deletionRadius = 5;
+        /// <summary>
+        /// The Position of the Cursor in the right picture box
+        /// </summary>
+        Point currentCursorPosition;
+        /// <summary>
+        /// The Previous Cursor Position in the right picture box
+        /// </summary>
+        Point previousCursorPosition;
+        /// <summary>
+        /// Queue for the cursorPositions
+        /// </summary>
+        Queue<Point> cursorPositions = new Queue<Point>();
+        /// <summary>
+        /// Lookup Matrix for checking postions of lines in the image
+        /// </summary>
+        bool[,] isFilledMatrix;
+        /// <summary>
+        /// Lookup Matrix for getting line ids at a certain postions of the image
+        /// </summary>
+        HashSet<int>[,] linesMatrix;
+        /// <summary>
+        /// Width of the LeftImageBox.
+        /// </summary>
+        public int leftImageBoxWidth;
+        /// <summary>
+        /// Height of the LeftImageBox.
+        /// </summary>
+        public int leftImageBoxHeight;
+        /// <summary>
+        /// Width of the RightImageBox.
+        /// </summary>
+        public int rightImageBoxWidth;
+        /// <summary>
+        /// Height of the RightImageBox.
+        /// </summary>
+        public int rightImageBoxHeight;
+
+        public ImageDimension leftImageSize { get; private set; }
+
+        public ImageDimension rightImageSize { get; private set; }
+        /// <summary>
+        /// Indicates whether or not the canvas on the right side is active.
+        /// </summary>
+        public bool canvasActive {get; set;}
+        /// <summary>
+        /// Indicates if there is a graphic loaded in the left canvas.
+        /// </summary>
+        public bool graphicLoaded { get; set; }
+        /// <summary>
+        /// Whether or not the mouse is pressed.
+        /// </summary>
+        private bool mouseDown;
+
+        List<InternalLine> leftLineList;
+        
+        List<Tuple<bool, InternalLine>> rightLineList;
+
+        List<Point> currentLine = new List<Point>();
+
+        public MVP_Model(MVP_Presenter presenter)
+        {
+            programPresenter = presenter;
+            historyOfActions = new ActionHistory();
+            //redrawAss = new RedrawAssistant();
+            //overlayItems = new List<Tuple<bool, HashSet<Point>>>();
+            rightLineList = new List<Tuple<bool, InternalLine>>();
+            canvasActive = false;
+            UpdateUI();
+            rightImageSize = new ImageDimension(0, 0);
+            leftImageSize = new ImageDimension(0, 0);
+        }
+
+        /**************************/
+        /*** INTERNAL FUNCTIONS ***/
+        /**************************/
+
+
+        /// <summary>
+        /// Change the status of whether or not the lines are shown.
+        /// </summary>
+        /// <param name="lines">The HashSet containing the affected Line IDs.</param>
+        /// <param name="shown">True if the lines should be shown, false if they should be hidden.</param>
+        private void ChangeLines(HashSet<int> lines, bool shown)
+        {
+            foreach (int lineId in lines)
+            {
+                if (lineId <= rightLineList.Count - 1 && lineId >= 0)
+                {
+                    rightLineList[lineId] = new Tuple<bool, InternalLine>(shown, rightLineList[lineId].Item2);
+                }
+            }
+        }
+
+        /// <summary>
+        /// A function that populates the matrixes needed for deletion detection with line data.
+        /// </summary>
+        private void RepopulateDeletionMatrixes()
+        {
+            if (canvasActive)
+            {
+                isFilledMatrix = new bool[rightImageSize.Width, rightImageSize.Height];
+                linesMatrix = new HashSet<int>[rightImageSize.Width, rightImageSize.Height];
+                foreach (Tuple<bool, InternalLine> lineTuple in rightLineList)
+                {
+                    if (lineTuple.Item1)
+                    {
+                        lineTuple.Item2.PopulateMatrixes(isFilledMatrix, linesMatrix);
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Tells the Presenter to Update the UI
+        /// </summary>
+        private void UpdateUI()
+        {
+            programPresenter.UpdateUIState(inDrawingMode, historyOfActions.CanUndo(), historyOfActions.CanRedo(), canvasActive, graphicLoaded);
+        }
+
+        /// <summary>
+        /// A function that checks the deletion matrixes at a certain point 
+        /// and returns all Line ids at that point and in a square around it in a certain range.
+        /// </summary>
+        /// <param name="p">The point around which to check.</param>
+        /// <param name="range">The range around the point. If range is 0, only the point is checked.</param>
+        /// <returns>A List of all lines.</returns>
+        private HashSet<int> CheckDeletionMatrixesAroundPoint(Point p, int range)
+        {
+            HashSet<int> returnSet = new HashSet<int>();
+
+            foreach (Point pnt in GeometryCalculator.FilledCircleAlgorithm(p, (int)range))
+            {
+                if (pnt.X >= 0 && pnt.Y >= 0 && pnt.X < rightImageSize.Width && pnt.Y < rightImageSize.Height)
+                {
+                    if (isFilledMatrix[(int)pnt.X, (int)pnt.Y])
+                    {
+                        returnSet.UnionWith(linesMatrix[(int)pnt.X, (int)pnt.Y]);
+                    }
+                }
+            }
+            return returnSet;
+        }
+
+        /********************************************/
+        /*** FUNCTIONS TO INTERACT WITH PRESENTER ***/
+        /********************************************/
+
+        /// <summary>
+        /// A function to update the dimensions of the left and right canvas when the window is resized.
+        /// </summary>
+        /// <param name="LeftCanvas">The size of the left canvas.</param>
+        /// <param name="RightCanvas">The size of the right canvas.</param>
+        public void ResizeEvent(ImageDimension LeftCanvas, ImageDimension RightCanvas)
+        {
+            if(LeftCanvas.Height >= 0 && LeftCanvas.Width>= 0) { leftImageSize = LeftCanvas; }
+            if(RightCanvas.Height >= 0 && RightCanvas.Width >= 0) { rightImageSize = RightCanvas; }
+            RepopulateDeletionMatrixes();
+        }
+
+        /// <summary>
+        /// A function to reset the right image.
+        /// </summary>
+        public void ResetRightImage()
+        {
+            rightLineList.Clear();
+            programPresenter.PassLastActionTaken(historyOfActions.Reset());
+            programPresenter.ClearRightLines();
+        }
+
+        /// <summary>
+        /// The function to set the left image.
+        /// </summary>
+        /// <param name="width">The width of the left image.</param>
+        /// <param name="height">The height of the left image.</param>
+        /// <param name="listOfLines">The List of Lines to be displayed in the left image.</param>
+        public void SetLeftLineList(int width, int height, List<InternalLine> listOfLines)
+        {
+            leftImageSize = new ImageDimension(width, height);
+            rightImageSize = new ImageDimension(width, height);
+            leftLineList = listOfLines;
+            graphicLoaded = true;
+            programPresenter.UpdateLeftLines(leftLineList);
+            CanvasActivated();
+        }
+
+        /// <summary>
+        /// A function to tell the model a new canvas was activated.
+        /// </summary>
+        public void CanvasActivated()
+        {
+            canvasActive = true;
+            RepopulateDeletionMatrixes();
+            UpdateUI();
+        }
+
+        /// <summary>
+        /// Will undo the last action taken, if the action history allows it.
+        /// </summary>
+        public void Undo()
+        {
+            if (historyOfActions.CanUndo())
+            {
+                HashSet<int> affectedLines = historyOfActions.GetCurrentAction().GetLineIDs();
+                SketchAction.ActionType undoAction = historyOfActions.GetCurrentAction().GetActionType();
+                switch (undoAction)
+                {
+                    case SketchAction.ActionType.Delete:
+                        //Deleted Lines need to be shown
+                        ChangeLines(affectedLines, true);
+                        break;
+                    case SketchAction.ActionType.Draw:
+                        //Drawn lines need to be hidden
+                        ChangeLines(affectedLines, false);
+                        break;
+                    default:
+                        break;
+                }
+                programPresenter.UpdateRightLines(rightLineList);
+            }
+            RepopulateDeletionMatrixes();
+            programPresenter.PassLastActionTaken(historyOfActions.MoveAction(true));
+            UpdateUI();
+        }
+
+        /// <summary>
+        /// Will redo the last action undone, if the action history allows it.
+        /// </summary>
+        public void Redo()
+        {
+            if (historyOfActions.CanRedo())
+            {
+                programPresenter.PassLastActionTaken(historyOfActions.MoveAction(false));
+                HashSet<int> affectedLines = historyOfActions.GetCurrentAction().GetLineIDs();
+                SketchAction.ActionType redoAction = historyOfActions.GetCurrentAction().GetActionType();
+                switch (redoAction)
+                {
+                    case SketchAction.ActionType.Delete:
+                        //Deleted Lines need to be redeleted
+                        ChangeLines(affectedLines, false);
+                        break;
+                    case SketchAction.ActionType.Draw:
+                        //Drawn lines need to be redrawn
+                        ChangeLines(affectedLines, true);
+                        break;
+                    default:
+                        break;
+                }
+                //TODO: For the person implementing overlay: Add check if overlay needs to be added
+                programPresenter.UpdateRightLines(rightLineList);
+                RepopulateDeletionMatrixes();
+            }
+            UpdateUI();
+        }
+
+        /// <summary>
+        /// The function called by the Presenter to change the drawing state of the program.
+        /// </summary>
+        /// <param name="nowDrawing">The new drawingstate of the program</param>
+        public void ChangeState(bool nowDrawing)
+        {
+            inDrawingMode = nowDrawing;
+            UpdateUI();
+        }
+        
+        /// <summary>
+        /// Updates the current cursor position of the model.
+        /// </summary>
+        /// <param name="p">The new cursor position</param>
+        public void SetCurrentCursorPosition(Point p)
+        {
+            currentCursorPosition = p;
+            //Temporary position of the optipoint change, change this when merging with optitrack branch
+            programPresenter.MoveOptiPoint(currentCursorPosition);
+            mouseDown = programPresenter.IsMousePressed();
+        }
+
+        /// <summary>
+        /// Start a new Line, when the Mouse is pressed down.
+        /// </summary>
+        public void MouseDown()
+        {
+            mouseDown = true;
+            if (inDrawingMode && mouseDown)
+            {
+                currentLine.Clear();
+                currentLine.Add(currentCursorPosition);
+            }
+        }
+
+        /// <summary>
+        /// Finish the current Line, when the pressed Mouse is released.
+        /// </summary>
+        /// <param name="valid">Whether the up event is valid or not</param>
+        public void MouseUp(bool valid)
+        {
+            mouseDown = false;
+            if (valid)
+            {
+                if (inDrawingMode && currentLine.Count > 0)
+                {
+                    InternalLine newLine = new InternalLine(currentLine, rightLineList.Count);
+                    rightLineList.Add(new Tuple<bool, InternalLine>(true, newLine));
+                    newLine.PopulateMatrixes(isFilledMatrix, linesMatrix);
+                    programPresenter.PassLastActionTaken(historyOfActions.AddNewAction(new SketchAction(SketchAction.ActionType.Draw, newLine.GetID())));
+                    programPresenter.UpdateRightLines(rightLineList);
+                    currentLine.Clear();
+                }
+            }
+            else
+            {
+                currentLine.Clear();
+            }
+            UpdateUI();
+        }
+
+        /// <summary>
+        /// Finish the current Line, when the pressed Mouse is released.
+        /// Overload that is used to pass a list of points to be used when one is available.
+        /// </summary>
+        /// <param name="p">The list of points</param>
+        public void MouseUp(List<Point> p)
+        {
+            mouseDown = false;
+            if (inDrawingMode && currentLine.Count > 0)
+            {
+                InternalLine newLine = new InternalLine(p, rightLineList.Count);
+                rightLineList.Add(new Tuple<bool, InternalLine>(true, newLine));
+                newLine.PopulateMatrixes(isFilledMatrix, linesMatrix);
+                programPresenter.PassLastActionTaken(historyOfActions.AddNewAction(new SketchAction(SketchAction.ActionType.Draw, newLine.GetID())));
+                programPresenter.UpdateRightLines(rightLineList);
+                currentLine.Clear();
+            }
+            UpdateUI();
+        }
+
+        /// <summary>
+        /// Method to be called every tick. Updates the current Line, or checks for Lines to delete, depending on the drawing mode.
+        /// </summary>
+        public void Tick()
+        {
+            if (cursorPositions.Count > 0) { previousCursorPosition = cursorPositions.Dequeue(); }
+            else { previousCursorPosition = currentCursorPosition; }
+            cursorPositions.Enqueue(currentCursorPosition);
+            //Drawing
+            if (inDrawingMode && programPresenter.IsMousePressed())
+            {
+                currentLine.Add(currentCursorPosition);
+                //programPresenter.UpdateCurrentLine(currentLine);
+            }
+            //Deleting
+            if (!inDrawingMode && programPresenter.IsMousePressed())
+            {
+                List<Point> uncheckedPoints = GeometryCalculator.BresenhamLineAlgorithm(previousCursorPosition, currentCursorPosition);
+                foreach (Point currPoint in uncheckedPoints)
+                {
+                    HashSet<int> linesToDelete = CheckDeletionMatrixesAroundPoint(currPoint, deletionRadius);
+                    if (linesToDelete.Count > 0)
+                    {
+                        programPresenter.PassLastActionTaken(historyOfActions.AddNewAction(new SketchAction(SketchAction.ActionType.Delete, linesToDelete)));
+                        foreach (int lineID in linesToDelete)
+                        {
+                            rightLineList[lineID] = new Tuple<bool, InternalLine>(false, rightLineList[lineID].Item2);
+                        }
+                        RepopulateDeletionMatrixes();
+                        //TODO: For the person implementing overlay: Add check if overlay needs to be added
+                        programPresenter.UpdateRightLines(rightLineList);
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// If there is unsaved progress.
+        /// </summary>
+        /// <returns>True if there is progress that has not been saved.</returns>
+        public bool HasUnsavedProgress()
+        {
+            return !historyOfActions.IsEmpty();
+        }
+    }
+}

+ 526 - 0
SketchAssistant/SketchAssistantWPF/MVP_Presenter.cs

@@ -0,0 +1,526 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Shapes;
+
+namespace SketchAssistantWPF
+{
+    public class MVP_Presenter
+    {
+        /// <summary>
+        /// The View of the MVP-Model, in this case Form1.
+        /// </summary>
+        MVP_View programView;
+        /// <summary>
+        /// The Model of the MVP-Model.
+        /// </summary>
+        MVP_Model programModel;
+        /// <summary>
+        /// A dictionary connecting the id of an InternalLine with the respective Polyline in the right canvas.
+        /// </summary>
+        Dictionary<int, Shape> rightPolyLines;
+
+        ImageDimension CanvasSizeLeft = new ImageDimension(0,0);
+
+        ImageDimension CanvasSizeRight = new ImageDimension(0, 0);
+
+        ImageDimension ImageSizeLeft = new ImageDimension(0, 0);
+
+        ImageDimension ImageSizeRight = new ImageDimension(0, 0);
+
+        List<double> ImageSimilarity = new List<double>();
+
+        List<InternalLine> LeftLines = new List<InternalLine>();
+
+        /*******************/
+        /*** ENUMERATORS ***/
+        /*******************/
+
+        public enum MouseAction
+        {
+            Down,
+            Up,
+            Up_Invalid,
+            Move
+        }
+
+        /***********************/
+        /*** CLASS VARIABLES ***/
+        /***********************/
+
+        /// <summary>
+        /// Instance of FileImporter to handle drawing imports.
+        /// </summary>
+        private FileImporter fileImporter;
+
+
+        public MVP_Presenter(MVP_View form)
+        {
+            programView = form;
+            programModel = new MVP_Model(this);
+            //Initialize Class Variables
+            fileImporter = new FileImporter();
+        }
+
+        /***********************************/
+        /*** FUNCTIONS VIEW -> PRESENTER ***/
+        /***********************************/
+
+        /// <summary>
+        /// Pass-trough function to update the appropriate information of the model, when the window is resized.
+        /// </summary>
+        /// <param name="leftPBS">The new size of the left picture box.</param>
+        /// <param name="rightPBS">The new size of the left picture box.</param>
+        public void Resize(Tuple<int, int> leftPBS, Tuple<int, int> rightPBS)
+        {
+            CanvasSizeLeft.ChangeDimension(leftPBS.Item1, leftPBS.Item2);
+            CanvasSizeRight.ChangeDimension(rightPBS.Item1, rightPBS.Item2);
+            //programModel.UpdateSizes(CanvasSizeRight);
+            programModel.ResizeEvent(CanvasSizeLeft, CanvasSizeRight);
+        }
+
+        /// <summary>
+        /// Display a new FileDialog to a svg drawing.
+        /// </summary>
+        /// <returns>True if loading was a success</returns>
+        public bool SVGToolStripMenuItemClick()
+        {
+            var okToContinue = true; bool returnval = false;
+            if (programModel.HasUnsavedProgress())
+            {
+                okToContinue = programView.ShowWarning("You have unsaved progress. Continue?");
+            }
+            if (okToContinue)
+            {
+                var fileNameTup = programView.openNewDialog("Scalable Vector Graphics|*.svg");
+                if (!fileNameTup.Item1.Equals("") && !fileNameTup.Item2.Equals(""))
+                {
+                    programView.SetToolStripLoadStatus(fileNameTup.Item2);
+                    try
+                    {
+                        Tuple<int, int, List<InternalLine>> values = fileImporter.ParseSVGInputFile(fileNameTup.Item1, programModel.leftImageBoxWidth, programModel.leftImageBoxHeight);
+                        values.Item3.ForEach(line => line.MakePermanent(0)); //Make all lines permanent
+                        programModel.SetLeftLineList(values.Item1, values.Item2, values.Item3);
+                        programModel.ResetRightImage();
+                        programModel.CanvasActivated();
+                        programModel.ChangeState(true);
+                        programView.EnableTimer();
+                        ClearRightLines();
+                        returnval = true;
+                    }
+                    catch (FileImporterException ex)
+                    {
+                        programView.ShowInfoMessage(ex.ToString());
+                    }
+                    catch (Exception ex)
+                    {
+                        programView.ShowInfoMessage("exception occured while trying to parse svg file:\n\n" + ex.ToString() + "\n\n" + ex.StackTrace);
+                    }
+                }
+            }
+            return returnval;
+        }
+
+        /// <summary>
+        /// Pass-trough function to change the drawing state of the model.
+        /// </summary>
+        /// <param name="NowDrawing">Indicates if the program is in drawing (true) or deletion (false) mode.</param>
+        public void ChangeState(bool NowDrawing)
+        {
+            programModel.ChangeState(NowDrawing);
+        }
+
+        /// <summary>
+        /// Pass-trough function to undo an action.
+        /// </summary>
+        public void Undo()
+        {
+            programModel.Undo();
+        }
+
+        /// <summary>
+        /// Pass-trough function to redo an action.
+        /// </summary>
+        public void Redo()
+        {
+            programModel.Redo();
+        }
+
+        /// <summary>
+        /// Pass-trough function for ticking the model.
+        /// </summary>
+        public void Tick()
+        {
+            programModel.Tick();
+        }
+
+        /// <summary>
+        /// Checks if there is unsaved progress, and promts the model to generate a new canvas if not.
+        /// </summary>
+        public void NewCanvas()
+        {
+            var okToContinue = true;
+            if (programModel.HasUnsavedProgress())
+            {
+                okToContinue = programView.ShowWarning("You have unsaved progress. Continue?");
+            }
+            if (okToContinue)
+            {
+                programModel.ResizeEvent(CanvasSizeLeft, CanvasSizeRight);
+                programModel.ResetRightImage();
+                programModel.CanvasActivated();
+                programModel.ChangeState(true);
+                programView.EnableTimer();
+                ClearRightLines();
+            }
+        }
+
+        /// <summary>
+        /// Pass-trough when the mouse is moved.
+        /// </summary>
+        /// <param name="mouseAction">The action which is sent by the View.</param>
+        /// <param name="e">The Mouse event arguments.</param>
+        public void MouseEvent(MouseAction mouseAction, Point position)
+        {
+            switch (mouseAction)
+            {
+                case MouseAction.Move:
+                    programModel.SetCurrentCursorPosition(position);
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// Pass-trough function that calls the correct Mouse event of the model, when the mouse is clicked.
+        /// </summary>
+        /// <param name="mouseAction">The action which is sent by the View.</param>
+        /// <param name="strokes">The Strokes.</param>
+        public void MouseEvent(MouseAction mouseAction, StrokeCollection strokes)
+        {
+
+            switch (mouseAction)
+            {
+                case MouseAction.Down:
+                    programModel.MouseDown();
+                    break;
+                case MouseAction.Up:
+                    if(strokes.Count > 0)
+                    {
+                        StylusPointCollection sPoints = strokes.First().StylusPoints;
+                        List<Point> points = new List<Point>();
+                        foreach(StylusPoint p in sPoints)
+                            points.Add(new Point(p.X, p.Y));
+                        programModel.MouseUp(points);
+                    }
+                    else
+                    {
+                        programModel.MouseUp(true);
+                    }
+                    break;
+                case MouseAction.Up_Invalid:
+                    programModel.MouseUp(false);
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        /************************************/
+        /*** FUNCTIONS MODEL -> PRESENTER ***/
+        /************************************/
+
+        /// <summary>
+        /// Return the position of the cursor
+        /// </summary>
+        /// <returns>The position of the cursor</returns>
+        public Point GetCursorPosition()
+        {
+            return programView.GetCursorPosition();
+        }
+
+        /// <summary>
+        /// Clears all Lines in the right canvas.
+        /// </summary>
+        public void ClearRightLines()
+        {
+            programView.RemoveAllRightLines();
+            rightPolyLines = new Dictionary<int, Shape>();
+            //Reset the similarity display
+            UpdateSimilarityScore(Double.NaN);
+        }
+
+        /// <summary>
+        /// A function to update the displayed lines in the right canvas.
+        /// </summary>
+        public void UpdateRightLines(List<Tuple<bool, InternalLine>> lines)
+        {
+            foreach(Tuple<bool, InternalLine> tup in lines)
+            {
+                var status = tup.Item1;
+                var line = tup.Item2;
+                if (!rightPolyLines.ContainsKey(line.GetID()))
+                {
+                    if (!line.isPoint)
+                    {
+                        Polyline newLine = new Polyline();
+                        newLine.Points = line.GetPointCollection();
+                        rightPolyLines.Add(line.GetID(), newLine);
+                        programView.AddNewLineRight(newLine);
+                    }
+                    else
+                    {
+                        Ellipse newPoint = new Ellipse();
+                        rightPolyLines.Add(line.GetID(), newPoint);
+                        programView.AddNewPointRight(newPoint, line);
+                    }
+                }
+                SetVisibility(rightPolyLines[line.GetID()], status);
+            }
+            //Calculate similarity scores 
+            UpdateSimilarityScore(Double.NaN); var templist = lines.Where(tup => tup.Item1).ToList();
+            if(LeftLines.Count > 0)
+            {
+                for(int i = 0; i < LeftLines.Count; i++)
+                {
+                    if (templist.Count == i) break;
+                    UpdateSimilarityScore(GeometryCalculator.CalculateSimilarity(templist[i].Item2, LeftLines[i]));
+                }
+            }
+            else if(templist.Count > 1)
+            {
+                UpdateSimilarityScore(GeometryCalculator.CalculateSimilarity(templist[templist.Count-2].Item2, templist[templist.Count-1].Item2));
+            }
+        }
+
+        /// <summary>
+        /// Updates the currentline
+        /// </summary>
+        /// <param name="linepoints">The points of the current line.</param>
+        public void UpdateCurrentLine(List<Point> linepoints)
+        {
+            Polyline currentLine = new Polyline();
+            currentLine.Stroke = Brushes.Black;
+            currentLine.Points = new PointCollection(linepoints);
+            programView.DisplayCurrLine(currentLine);
+        }
+
+        /// <summary>
+        /// A function to update the displayed lines in the left canvas.
+        /// </summary>
+        public void UpdateLeftLines(List<InternalLine> lines)
+        {
+            programView.RemoveAllLeftLines();
+            foreach (InternalLine line in lines)
+            {
+                Polyline newLine = new Polyline();
+                newLine.Stroke = Brushes.Black;
+                newLine.Points = line.GetPointCollection();
+                programView.AddNewLineLeft(newLine);
+            }
+            programView.SetCanvasState("LeftCanvas", true);
+            programView.SetCanvasState("RightCanvas", true);
+
+            LeftLines = lines;
+        }
+
+        /// <summary>
+        /// Called by the model when the state of the Program changes. 
+        /// Changes the look of the UI according to the current state of the model.
+        /// </summary>
+        /// <param name="inDrawingMode">If the model is in Drawing Mode</param>
+        /// <param name="canUndo">If actions in the model can be undone</param>
+        /// <param name="canRedo">If actions in the model can be redone</param>
+        /// <param name="canvasActive">If the right canvas is active</param>
+        /// <param name="graphicLoaded">If an image is loaded in the model</param>
+        public void UpdateUIState(bool inDrawingMode, bool canUndo, bool canRedo, bool canvasActive, bool graphicLoaded)
+        {
+            Dictionary<String, MainWindow.ButtonState> dict = new Dictionary<String, MainWindow.ButtonState> {
+                {"canvasButton", MainWindow.ButtonState.Enabled }, {"drawButton", MainWindow.ButtonState.Disabled}, {"deleteButton",MainWindow.ButtonState.Disabled },
+                {"undoButton", MainWindow.ButtonState.Disabled },{"redoButton",  MainWindow.ButtonState.Disabled}};
+
+            if (canvasActive)
+            {
+                if (inDrawingMode)
+                {
+                    dict["drawButton"] = MainWindow.ButtonState.Active;
+                    dict["deleteButton"] = MainWindow.ButtonState.Enabled;
+                }
+                else
+                {
+                    dict["drawButton"] = MainWindow.ButtonState.Enabled;
+                    dict["deleteButton"] = MainWindow.ButtonState.Active;
+                }
+                if (canUndo) { dict["undoButton"] = MainWindow.ButtonState.Enabled; }
+                if (canRedo) { dict["redoButton"] = MainWindow.ButtonState.Enabled; }
+            }
+            foreach (KeyValuePair<String, MainWindow.ButtonState> entry in dict)
+            {
+                programView.SetToolStripButtonStatus(entry.Key, entry.Value);
+            }
+            programView.SetCanvasState("RightCanvas", canvasActive);
+            programView.SetCanvasState("LeftCanvas", graphicLoaded);
+        }
+
+
+        /// <summary>
+        /// Pass-trough function to display an info message in the view.
+        /// </summary>
+        /// <param name="msg">The message.</param>
+        public void PassMessageToView(String msg)
+        {
+            programView.ShowInfoMessage(msg);
+        }
+
+        /// <summary>
+        /// Pass-trough function to update the display of the last action taken.
+        /// </summary>
+        /// <param name="msg">The new last action taken.</param>
+        public void PassLastActionTaken(String msg)
+        {
+            programView.SetLastActionTakenText(msg);
+        }
+
+        /// <summary>
+        /// Passes whether or not the mouse is pressed.
+        /// </summary>
+        /// <returns>Whether or not the mouse is pressed</returns>
+        public bool IsMousePressed()
+        {
+            return programView.IsMousePressed();
+        }
+
+        /// <summary>
+        /// Update the similarity score displayed in the UI.
+        /// </summary>
+        /// <param name="score">Score will be reset if NaN is passed, 
+        /// will be ignored if the score is not between 0 and 1</param>
+        public void UpdateSimilarityScore(double score)
+        {
+            if (Double.IsNaN(score))
+            {
+                ImageSimilarity.Clear();
+                programView.SetImageSimilarityText("");
+            }
+            else
+            {
+                if (score >= 0 && score <= 1) ImageSimilarity.Add(score);
+                programView.SetImageSimilarityText((ImageSimilarity.Sum() / ImageSimilarity.Count).ToString());
+            }
+        }
+
+        /// <summary>
+        /// Change the properties of an overlay item.
+        /// </summary>
+        /// <param name="name">The name of the item in the overlay dictionary</param>
+        /// <param name="visible">If the element should be visible</param>
+        /// <param name="position">The new position of the element</param>
+        public void SetOverlayStatus(String name, bool visible, Point position)
+        {
+            Shape shape = new Ellipse(); double visibility = 1; double xDif = 0; double yDif = 0;
+            switch (name)
+            {
+                case "optipoint":
+                    shape = ((MainWindow)programView).OverlayDictionary["optipoint"];
+                    visibility = 0.5; xDif = 2.5; yDif = 2.5;
+                    break;
+                case "startpoint":
+                    shape = ((MainWindow)programView).OverlayDictionary["startpoint"];
+                    xDif = ((MainWindow)programView).markerRadius; yDif = xDif;
+                    break;
+                case "endpoint":
+                    shape = ((MainWindow)programView).OverlayDictionary["endpoint"];
+                    xDif = ((MainWindow)programView).markerRadius; yDif = xDif;
+                    break;
+                default:
+                    Console.WriteLine("Unknown Overlay Item. Please check in MVP_Presenter if this item exists.");
+                    return;
+            }
+            if (visible) shape.Opacity = visibility;
+            else shape.Opacity = 0.00001;
+            Point point = ConvertRightCanvasCoordinateToOverlay(position);
+            shape.Margin = new Thickness(point.X - xDif, point.Y - yDif, 0, 0);
+        }
+
+        /// <summary>
+        /// Change the properties of an overlay item.
+        /// </summary>
+        /// <param name="name">The name of the item in the overlay dictionary</param>
+        /// <param name="visible">If the element should be visible</param>
+        /// <param name="pos1">The new position of the element</param>
+        /// <param name="pos2">The new position of the element</param>
+        public void SetOverlayStatus(String name, bool visible, Point pos1, Point pos2)
+        {
+            Shape shape = new Ellipse();
+            switch (name.Substring(0,7))
+            {
+                case "dotLine":
+                    if (((MainWindow)programView).OverlayDictionary.ContainsKey(name))
+                    { shape = ((MainWindow)programView).OverlayDictionary[name];
+                        break;
+                    }
+                    goto default;
+                default:
+                    SetOverlayStatus(name, visible, pos1);
+                    return;
+            }
+            if (visible) shape.Opacity = 1;
+            else shape.Opacity = 0.00001;
+            Point p1 = ConvertRightCanvasCoordinateToOverlay(pos1); Point p2 = ConvertRightCanvasCoordinateToOverlay(pos2);
+            ((Line)shape).X1 = p1.X; ((Line)shape).X2 = p2.X; ((Line)shape).Y1 = p1.Y; ((Line)shape).Y2 = p2.Y;
+        }
+
+        /// <summary>
+        /// Move the optitrack pointer to a new position.
+        /// </summary>
+        /// <param name="position">Position relative to the Rightcanvas</param>
+        public void MoveOptiPoint(Point position)
+        {
+            Point point = ConvertRightCanvasCoordinateToOverlay(position);
+            Shape shape = ((MainWindow)programView).OverlayDictionary["optipoint"];
+            shape.Margin = new Thickness(point.X - 2.5, point.Y - 2.5, 0, 0);
+        }
+
+        /*************************/
+        /*** HELPING FUNCTIONS ***/
+        /*************************/
+
+        /// <summary>
+        /// Convert point relative to the right canvas to a point relative to the overlay canvas.
+        /// </summary>
+        /// <param name="p">The point to convert.</param>
+        /// <returns>The respective overlay canvas.</returns>
+        private Point ConvertRightCanvasCoordinateToOverlay(Point p)
+        {
+            MainWindow main = (MainWindow)programView;
+            double xDif = (main.CanvasLeftEdge.ActualWidth + main.LeftCanvas.ActualWidth + main.CanvasSeperator.ActualWidth);
+            double yDif = (main.ButtonToolbar.ActualHeight);
+            return new Point(p.X + xDif,p.Y + yDif);
+        }
+
+        /// <summary>
+        /// Sets the visibility of a polyline.
+        /// </summary>
+        /// <param name="line">The polyline</param>
+        /// <param name="visible">Whether or not it should be visible.</param>
+        private void SetVisibility(Shape line, bool visible)
+        {
+            if (!visible)
+            {
+                line.Opacity = 0.00001;
+            }
+            else
+            {
+                line.Opacity = 1;
+            }
+        }
+    }
+}

+ 133 - 0
SketchAssistant/SketchAssistantWPF/MVP_View.cs

@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Ink;
+using System.Windows.Shapes;
+
+namespace SketchAssistantWPF
+{
+    public interface MVP_View
+    {
+        /// <summary>
+        /// Updates the colour of a canvas.
+        /// </summary>
+        /// <param name="canvasName">The name of the canvas to be updated.</param>
+        /// <param name="active">Whether or not the canvas is active.</param>
+        void SetCanvasState(string canvasName, bool active);
+
+        /// <summary>
+        /// Remove the current line.
+        /// </summary>
+        void RemoveCurrLine();
+
+        /// <summary>
+        /// Display the current line.
+        /// </summary>
+        /// <param name="line">The current line to display</param>
+        void DisplayCurrLine(Polyline line);
+
+        /// <summary>
+        /// Removes all Lines from the left canvas.
+        /// </summary>
+        void RemoveAllLeftLines();
+
+        /// <summary>
+        /// Removes all lines in the right canvas.
+        /// </summary>
+        void RemoveAllRightLines();
+
+        /// <summary>
+        /// Adds another Line that will be displayed in the left display.
+        /// </summary>
+        /// <param name="newLine">The new Polyline to be added displayed.</param>
+        void AddNewLineLeft(Polyline newLine);
+
+        /// <summary>
+        /// Adds another Line that will be displayed in the right display.
+        /// </summary>
+        /// <param name="newLine">The new Polyline to be added displayed.</param>
+        void AddNewLineRight(Polyline newLine);
+
+        /// <summary>
+        /// Enables the timer of the View, which will tick the Presenter.
+        /// </summary>
+        void EnableTimer();
+
+        /// <summary>
+        /// A function that opens a file dialog and returns the filename.
+        /// </summary>
+        /// <param name="Filter">The filter that should be applied to the new Dialog.</param>
+        /// <returns>Returns the FileName and the SafeFileName if the user correctly selects a file, 
+        /// else returns a tuple with empty strigns</returns>
+        Tuple<String, String> openNewDialog(String Filter);
+
+        /// <summary>
+        /// Sets the contents of the load status indicator label.
+        /// </summary>
+        /// <param name="message">The new contents</param>
+        void SetToolStripLoadStatus(String message);
+
+        /// <summary>
+        /// Sets the contents of the last action taken indicator label.
+        /// </summary>
+        /// <param name="message">The new contents</param>
+        void SetLastActionTakenText(String message);
+
+        /// <summary>
+        /// Changes the states of a tool strip button.
+        /// </summary>
+        /// <param name="buttonName">The name of the button.</param>
+        /// <param name="state">The new state of the button.</param>
+        void SetToolStripButtonStatus(String buttonName, MainWindow.ButtonState state);
+
+        /// <summary>
+        /// shows the given info message in a popup and asks the user to aknowledge it
+        /// </summary>
+        /// <param name="message">the message to show</param>
+        void ShowInfoMessage(String message);
+
+        /// <summary>
+        /// Shows a warning box with the given message (Yes/No Buttons)and returns true if the user aknowledges it.
+        /// </summary>
+        /// <param name="message">The message of the warning.</param>
+        /// <returns>True if the user confirms (Yes), negative if he doesn't (No)</returns>
+        bool ShowWarning(String message);
+
+        /// <summary>
+        /// If the mouse is pressed or not.
+        /// </summary>
+        /// <returns>Whether or not the mouse is pressed.</returns>
+        bool IsMousePressed();
+
+        /// <summary>
+        /// Adds a point to the right canvas
+        /// </summary>
+        /// <param name="newPoint">The point</param>
+        /// <param name="line">The original line object</param>
+        void AddNewPointRight(Ellipse newPoint, InternalLine line);
+
+        /// <summary>
+        /// Adds a point to the left canvas
+        /// </summary>
+        /// <param name="newPoint">The point</param>
+        void AddNewPointLeft(Ellipse newPoint);
+
+        /// <summary>
+        /// Returns the cursor position.
+        /// </summary>
+        /// <returns>The cursor Position</returns>
+        Point GetCursorPosition();
+
+        /// <summary>
+        /// Sets the contents of the status bar label containing
+        /// the similarity score of the left and right image.
+        /// </summary>
+        /// <param name="message">The message to be set, 
+        /// will be set to the default value if left empty.</param>
+        void SetImageSimilarityText(string message);
+    }
+}

+ 196 - 0
SketchAssistant/SketchAssistantWPF/MainWindow.xaml

@@ -0,0 +1,196 @@
+<Window x:Class="SketchAssistantWPF.MainWindow"
+        xmlns:local="clr-namespace:SketchAssistantWPF"
+        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        mc:Ignorable="d"
+        Title="Sketch Assistant" Height="612" Width="914" SizeChanged="Window_SizeChanged">
+    <Grid x:Name="RootGrid">
+        <Grid.ColumnDefinitions>
+            <ColumnDefinition Width="5"/>
+            <ColumnDefinition Width="*"/>
+            <ColumnDefinition Width="5"/>
+            <ColumnDefinition Width="*"/>
+            <ColumnDefinition Width="5"/>
+        </Grid.ColumnDefinitions>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="auto"/>
+            <RowDefinition Height="*"/>
+            <RowDefinition Height="auto"/>
+        </Grid.RowDefinitions>
+        <StackPanel x:Name="MenuToolbar" Orientation="Horizontal" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" Background="LightGray">
+            <Menu Background="LightGray"   VerticalAlignment="Center" Padding="5,0,0,0">
+                <MenuItem x:Name="LoadMenuButton" Header="Load">
+                    <MenuItem x:Name="SVGMenuButton" Header="Import SVG File" Click="SVGMenuItem_Click"/>
+                </MenuItem>
+                <MenuItem x:Name="EditMenuButton" Header="Edit">
+                    <MenuItem x:Name="CanvasMenuButton" Header="New Canvas" Click="CanvasButton_Click"/>
+                    <MenuItem x:Name="UndoMenuButton" Header="Undo" Click="UndoButton_Click"/>
+                    <MenuItem x:Name="RedoMenuButton" Header="Redo" Click="RedoButton_Click"/>
+                    <MenuItem x:Name="DebugMode" Header="Debug Input">
+                        <MenuItem x:Name="DebugOne" Header="Debug 1" Click="DebugOne_Click"/>
+                        <MenuItem x:Name="DebugTwo" Header="Debug 2" Click="DebugTwo_Click"/>
+                        <MenuItem x:Name="DebugThree" Header="Debug 3" Click="DebugThree_Click"/>
+                        <MenuItem x:Name="DebugFour" Header="Debug 4" Click="DebugFour_Click"/>
+                    </MenuItem>
+                </MenuItem>
+            </Menu>
+        </StackPanel>
+        <StackPanel x:Name="ButtonToolbar" Orientation="Horizontal" Grid.Column="2" Grid.Row="0" Grid.ColumnSpan="3" Background="LightGray">
+            <Canvas Name="ToolbarSpacer" Width="5" Background="LightGray" />
+            <!-- All Icons in the StackPanel taken from openclipart.org -->
+            <Button x:Name="CanvasButton" ToolTip="Create a new Canvas" Click="CanvasButton_Click" BorderThickness="0">
+                <Rectangle Width="30" Height="30">
+                    <Rectangle.Fill>
+                        <DrawingBrush>
+                            <DrawingBrush.Drawing>
+                                <DrawingGroup ClipGeometry="M0,0 V60 H60 V0 H0 Z">
+                                    <DrawingGroup Transform="1.0513,0,0,1.0513,-1.5376,-1.5376">
+                                        <GeometryDrawing Geometry="F1 M60,60z M0,0z M11.75,54.375L11.75,5.625 39.438,5.625 48.25,14.438 48.25,54.375 11.75,54.375z">
+                                            <GeometryDrawing.Pen>
+                                                <Pen Brush="#FFFFFFFF" Thickness="7.1343" StartLineCap="Round" EndLineCap="Round" LineJoin="Round" />
+                                            </GeometryDrawing.Pen>
+                                        </GeometryDrawing>
+                                        <GeometryDrawing Brush="#FFFFFFFF" Geometry="F1 M60,60z M0,0z M11.75,54.375L11.75,5.625 39.438,5.625 48.25,14.438 48.25,54.375 11.75,54.375z">
+                                            <GeometryDrawing.Pen>
+                                                <Pen Brush="#FF000000" Thickness="2.3781" StartLineCap="Round" EndLineCap="Round" LineJoin="Round" />
+                                            </GeometryDrawing.Pen>
+                                        </GeometryDrawing>
+                                        <GeometryDrawing Brush="#FF000000" Geometry="F1 M60,60z M0,0z M39.437,14.438L39.437,5.625 48.25,14.438 39.437,14.438z">
+                                            <GeometryDrawing.Pen>
+                                                <Pen Brush="#FF000000" Thickness="2.3781" StartLineCap="Round" EndLineCap="Round" LineJoin="Round" />
+                                            </GeometryDrawing.Pen>
+                                        </GeometryDrawing>
+                                    </DrawingGroup>
+                                </DrawingGroup>
+                            </DrawingBrush.Drawing>
+                        </DrawingBrush>
+                    </Rectangle.Fill>
+                </Rectangle>
+            </Button>
+            <ToggleButton x:Name="DrawButton" ToolTip="Enter Drawing Mode" Click="DrawButton_Click" BorderThickness="0">
+                <Rectangle Width="30" Height="30">
+                    <Rectangle.Fill>
+                        <DrawingBrush>
+                            <DrawingBrush.Drawing>
+                                <DrawingGroup ClipGeometry="M0,0 V100 H100 V0 H0 Z">
+                                    <GeometryDrawing Brush="#FF000000" Geometry="F1 M100,100z M0,0z M69.172,11.265C68.981,11.073,68.67,11.073,68.477,11.265L11.639,68.029C11.447,68.22,11.447,68.53,11.639,68.721L30.846,87.905C30.942,88 31.068,88.048 31.194,88.048 31.319,88.048 31.445,88 31.541,87.905L88.38,31.14C88.473,31.048 88.525,30.924 88.525,30.793 88.525,30.662 88.473,30.538 88.38,30.446L69.172,11.265z" />
+                                    <GeometryDrawing Brush="#FF000000" Geometry="F1 M100,100z M0,0z M96.846,9.12L90.527,2.809C88.713,0.997999999999999 86.301,-8.88178419700125E-16 83.735,-8.88178419700125E-16 81.171,-8.88178419700125E-16 78.758,0.997999999999999 76.945,2.809L70.626,9.12C70.533,9.212 70.481,9.336 70.481,9.467 70.481,9.597 70.533,9.721 70.626,9.813L89.833,28.995C89.925,29.087 90.051,29.139 90.181,29.139 90.312,29.139 90.436,29.087 90.527,28.995L96.846,22.684C98.66,20.872 99.659,18.464 99.659,15.901 99.659,13.34 98.66,10.932 96.846,9.12z" />
+                                    <GeometryDrawing Brush="#FF000000" Geometry="F1 M100,100z M0,0z M10.358,69.967C10.236,69.846 10.062,69.795 9.894,69.838 9.727,69.878 9.594,70.003 9.543,70.168L0.363,99.363C0.308,99.541 0.357,99.732 0.49,99.861 0.584,99.952 0.707,100 0.832,100 0.886,100 0.942,99.989 0.996,99.971L29.383,89.959C29.542,89.903 29.661,89.771 29.698,89.605 29.735,89.441 29.686,89.268 29.566,89.15L10.358,69.967z" />
+                                </DrawingGroup>
+                            </DrawingBrush.Drawing>
+                        </DrawingBrush>
+                    </Rectangle.Fill>
+                </Rectangle>
+            </ToggleButton>
+            <ToggleButton x:Name="DeleteButton" ToolTip="Enter Deletion Mode" Click="DeleteButton_Click" BorderThickness="0">
+                <Rectangle Width="30" Height="30">
+                    <Rectangle.Fill>
+                        <DrawingBrush>
+                            <DrawingBrush.Drawing>
+                                <DrawingGroup ClipGeometry="M0,0 V60 H60 V0 H0 Z">
+                                    <DrawingGroup>
+                                        <GeometryDrawing Geometry="F1 M60,60z M0,0z M41.312,11.031L6.438,32.5C5.407,33.151,4.6875,34.249,4.6875,35.562L4.6875,45.281C4.6875,47.32,6.3367,48.969,8.375,48.969L24.656,48.969C25.444,48.969,26.121,48.669,26.719,48.25L54.312,26.719 55.312,11.094 41.312,11.031z">
+                                            <GeometryDrawing.Pen>
+                                                <Pen Brush="#FFFFFFFF" Thickness="8.125" StartLineCap="Round" EndLineCap="Round" LineJoin="Round" />
+                                            </GeometryDrawing.Pen>
+                                        </GeometryDrawing>
+                                        <DrawingGroup Transform="0.99664,0,0,0.99664,-0.3563,-0.39594">
+                                            <GeometryDrawing Brush="#FF000000" Geometry="F0 M60,60z M0,0z M27.173,48.89L54.852,27.221 55.857,11.533 27.522,33.19 27.173,48.89z">
+                                                <GeometryDrawing.Pen>
+                                                    <Pen Brush="#FF000000" Thickness="3.1356" StartLineCap="Round" EndLineCap="Round" LineJoin="Round" />
+                                                </GeometryDrawing.Pen>
+                                            </GeometryDrawing>
+                                            <GeometryDrawing Brush="#FFFFFFFF" Geometry="F0 M60,60z M0,0z M6.6373,32.995L41.8073,11.464 55.8573,11.533 27.5223,33.19 6.63729999999999,32.995z">
+                                                <GeometryDrawing.Pen>
+                                                    <Pen Brush="#FF000000" Thickness="3.1356" StartLineCap="Round" EndLineCap="Round" LineJoin="Round" />
+                                                </GeometryDrawing.Pen>
+                                            </GeometryDrawing>
+                                            <GeometryDrawing>
+                                                <GeometryDrawing.Brush>
+                                                    <SolidColorBrush Color="#FFFFFFFF" Opacity="1" Transform="1.8458,0,0,1.8458,-121.06,-6.2579" />
+                                                </GeometryDrawing.Brush>
+                                                <GeometryDrawing.Pen>
+                                                    <Pen Brush="#FF000000" Thickness="1.6987" StartLineCap="Flat" EndLineCap="Flat" LineJoin="Round" />
+                                                </GeometryDrawing.Pen>
+                                                <GeometryDrawing.Geometry>
+                                                    <PathGeometry FillRule="EvenOdd" Transform="1.8458,0,0,1.8458,-121.06,-6.2579" Figures="M60,60z M0,0z M70.328,20.937C69.22,20.937,68.328,21.829,68.328,22.937L68.328,28.226C68.328,29.334,69.22,30.226,70.328,30.226L79.18,30.226C80.288,30.226,81.18,29.334,81.18,28.226L81.18,22.937C81.18,21.829,80.288,20.937,79.18,20.937L70.328,20.937z" />
+                                                </GeometryDrawing.Geometry>
+                                            </GeometryDrawing>
+                                        </DrawingGroup>
+                                    </DrawingGroup>
+                                </DrawingGroup>
+                            </DrawingBrush.Drawing>
+                        </DrawingBrush>
+                    </Rectangle.Fill>
+                </Rectangle>
+            </ToggleButton>
+            <Button x:Name="UndoButton" ToolTip="Undo the last action" Click="UndoButton_Click" BorderThickness="0">
+                <Rectangle Width="30" Height="30">
+                    <Rectangle.Fill>
+                        <DrawingBrush>
+                            <DrawingBrush.Drawing>
+                                <DrawingGroup ClipGeometry="M0,0 V60 H60 V0 H0 Z">
+                                    <GeometryDrawing Geometry="F1 M60,60z M0,0z M25.063,4.6882L9.445,20.1632 25.063,35.6012 25.063,25.9792C25.442,25.9292 25.778,25.7632 26.176,25.7632 33.176,25.7632 38.85,31.4742 38.85,38.4732 38.85,41.7872 37.521,44.7602 35.439,47.0182L43.733,55.3122C47.934,50.9322 50.555,45.0212 50.555,38.4732 50.555,25.0092 39.64,14.0592 26.176,14.0592 25.797,14.0592 25.437,14.1452 25.063,14.1672L25.063,4.68840000000001z">
+                                        <GeometryDrawing.Pen>
+                                            <Pen Brush="#FFFFFFFF" Thickness="8.125" StartLineCap="Round" EndLineCap="Round" LineJoin="Round" />
+                                        </GeometryDrawing.Pen>
+                                    </GeometryDrawing>
+                                    <GeometryDrawing Brush="#FF000000" Geometry="F0 M60,60z M0,0z M25.063,4.6882L9.445,20.1632 25.063,35.6012 25.063,25.9792C25.442,25.9292 25.778,25.7632 26.176,25.7632 33.176,25.7632 38.85,31.4742 38.85,38.4732 38.85,41.7872 37.521,44.7602 35.439,47.0182L43.733,55.3122C47.934,50.9322 50.555,45.0212 50.555,38.4732 50.555,25.0092 39.64,14.0592 26.176,14.0592 25.797,14.0592 25.437,14.1452 25.063,14.1672L25.063,4.68840000000001z">
+                                        <GeometryDrawing.Pen>
+                                            <Pen Brush="#FF000000" Thickness="3.125" StartLineCap="Round" EndLineCap="Round" LineJoin="Round" />
+                                        </GeometryDrawing.Pen>
+                                    </GeometryDrawing>
+                                </DrawingGroup>
+                            </DrawingBrush.Drawing>
+                        </DrawingBrush>
+                    </Rectangle.Fill>
+                </Rectangle>
+            </Button>
+            <Button x:Name="RedoButton" ToolTip="Redo the last undone action" Click="RedoButton_Click" BorderThickness="0">
+                <Rectangle Width="30" Height="30">
+                    <Rectangle.Fill>
+                        <DrawingBrush>
+                            <DrawingBrush.Drawing>
+                                <DrawingGroup ClipGeometry="M0,0 V60 H60 V0 H0 Z">
+                                    <DrawingGroup Transform="-1,0,0,1,60,0">
+                                        <GeometryDrawing Geometry="F1 M60,60z M0,0z M25.063,4.6882L9.445,20.1632 25.063,35.6012 25.063,25.9792C25.442,25.9292 25.778,25.7632 26.176,25.7632 33.176,25.7632 38.85,31.4742 38.85,38.4732 38.85,41.7872 37.521,44.7602 35.439,47.0182L43.733,55.3122C47.934,50.9322 50.555,45.0212 50.555,38.4732 50.555,25.0092 39.64,14.0592 26.176,14.0592 25.797,14.0592 25.437,14.1452 25.063,14.1672L25.063,4.68840000000001z">
+                                            <GeometryDrawing.Pen>
+                                                <Pen Brush="#FFFFFFFF" Thickness="8.125" StartLineCap="Round" EndLineCap="Round" LineJoin="Round" />
+                                            </GeometryDrawing.Pen>
+                                        </GeometryDrawing>
+                                        <GeometryDrawing Brush="#FF000000" Geometry="F0 M60,60z M0,0z M25.063,4.6882L9.445,20.1632 25.063,35.6012 25.063,25.9792C25.442,25.9292 25.778,25.7632 26.176,25.7632 33.176,25.7632 38.85,31.4742 38.85,38.4732 38.85,41.7872 37.521,44.7602 35.439,47.0182L43.733,55.3122C47.934,50.9322 50.555,45.0212 50.555,38.4732 50.555,25.0092 39.64,14.0592 26.176,14.0592 25.797,14.0592 25.437,14.1452 25.063,14.1672L25.063,4.68840000000001z">
+                                            <GeometryDrawing.Pen>
+                                                <Pen Brush="#FF000000" Thickness="3.125" StartLineCap="Round" EndLineCap="Round" LineJoin="Round" />
+                                            </GeometryDrawing.Pen>
+                                        </GeometryDrawing>
+                                    </DrawingGroup>
+                                </DrawingGroup>
+                            </DrawingBrush.Drawing>
+                        </DrawingBrush>
+                    </Rectangle.Fill>
+                </Rectangle>
+            </Button>
+        </StackPanel>
+        <Canvas Name="CanvasLeftEdge" Grid.Column="0" Grid.Row="1" Background="LightGray" />
+        <Canvas x:Name="LeftCanvas" Background="SlateGray" Grid.Column="1" Grid.Row="1" Height="auto" Grid.ColumnSpan="1"/>
+        <Canvas Name="CanvasSeperator" Grid.Column="2" Grid.Row="1" Background="LightGray" />
+        <InkCanvas x:Name="RightCanvas" Background="SlateGray" Grid.Column="3" Grid.Row="1" Height="auto"
+            PreviewMouseDown="RightCanvas_MouseDown" MouseUp="RightCanvas_MouseUp" MouseMove="RightCanvas_MouseMove" Grid.ColumnSpan="2" 
+                    StrokeCollected="RightCanvas_StrokeCollection" EditingMode="None" IsStylusCapturedChanged="RightCanvas_IsStylusCapturedChanged"/>
+        <Canvas Name="CanvasRightEdge" Grid.Column="4" Grid.Row="1" Background="LightGray" />
+
+        <DockPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="8">
+            <StatusBar DockPanel.Dock="Bottom" Name="StatusBar"  Background="LightGray">
+                <TextBox Name="LoadStatusBox" Text="nothing loaded" Background="LightGray"/>
+                <Separator/>
+                <TextBox Name="LastActionBox" Text="none" Background="LightGray"/>
+                <Separator/>
+                <TextBox Name="LineSimilarityBox" Text="-" Background="LightGray"/>
+            </StatusBar>
+        </DockPanel>
+        <Ellipse x:Name="optipoint" Opacity="0.00001" Height="5" Width="5" Fill="Black"/>
+        <Canvas x:Name="OverlayCanvas" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="5" Grid.RowSpan="3"/>
+    </Grid>
+</Window>

+ 668 - 0
SketchAssistant/SketchAssistantWPF/MainWindow.xaml.cs

@@ -0,0 +1,668 @@
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Timers;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using System.Windows.Threading;
+using System.Windows.Ink;
+using System.Windows.Media.Effects;
+
+namespace SketchAssistantWPF
+{
+    /// <summary>
+    /// Interaction logic for MainWindow.xaml
+    /// </summary>
+    public partial class MainWindow : Window, MVP_View
+    {
+        public MainWindow()
+        {
+            bool InDebugMode = false;
+            String[] commArgs = Environment.GetCommandLineArgs();
+            InitializeComponent();
+            if (commArgs.Length > 1)
+            {
+                if (commArgs[1].Equals("-debug"))
+                {
+                    InDebugMode = true;
+                }
+            }
+            if(!InDebugMode)
+            {
+                DebugMode.Visibility = Visibility.Collapsed;
+            }
+            ProgramPresenter = new MVP_Presenter(this);
+            //  DispatcherTimer setup
+            dispatcherTimer = new DispatcherTimer(DispatcherPriority.Render);
+            dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
+            dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 10);
+            ProgramPresenter.Resize(new Tuple<int, int>((int)LeftCanvas.Width, (int)LeftCanvas.Height),
+                new Tuple<int, int>((int)RightCanvas.Width, (int)RightCanvas.Height));
+            //Setup overlay items
+            SetupOverlay();
+        }
+
+        public enum ButtonState
+        {
+            Enabled,
+            Disabled,
+            Active
+        }
+
+        DispatcherTimer dispatcherTimer;
+        /// <summary>
+        /// Dialog to select a file.
+        /// </summary>
+        OpenFileDialog openFileDialog = new OpenFileDialog();
+        /// <summary>
+        /// All Lines in the current session
+        /// </summary>
+        List<Tuple<bool, InternalLine>> rightLineList = new List<Tuple<bool, InternalLine>>();
+        /// <summary>
+        /// Queue for the cursorPositions
+        /// </summary>
+        Queue<Point> cursorPositions = new Queue<Point>();
+        /// <summary>
+        /// The Presenter Component of the MVP-Model
+        /// </summary>
+        MVP_Presenter ProgramPresenter;
+        /// <summary>
+        /// The line currently being drawn
+        /// </summary>
+        Polyline currentLine;
+        /// <summary>
+        /// If the debug function is running.
+        /// </summary>
+        bool debugRunning = false;
+        /// <summary>
+        /// Point collections for debugging.
+        /// </summary>
+        DebugData debugDat = new DebugData();
+        /// <summary>
+        /// Stores Lines drawn on RightCanvas.
+        /// </summary>
+        public StrokeCollection strokeCollection = new StrokeCollection();
+        /// <summary>
+        /// Size of areas marking endpoints of lines in the redraw mode.
+        /// </summary>
+        public int markerRadius = 5;
+        /// <summary>
+        /// Dictionary containing the overlay elements
+        /// </summary>
+        public Dictionary<String, Shape> OverlayDictionary = new Dictionary<string, Shape>();
+
+        /********************************************/
+        /*** WINDOW SPECIFIC FUNCTIONS START HERE ***/
+        /********************************************/
+
+        /// <summary>
+        /// Resize Function connected to the form resize event, will refresh the form when it is resized
+        /// </summary>
+        private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
+        {
+            ProgramPresenter.Resize(new Tuple<int, int>((int)LeftCanvas.ActualWidth, (int)LeftCanvas.ActualHeight),
+                new Tuple<int, int>((int)RightCanvas.ActualWidth, (int)RightCanvas.ActualHeight));
+        }
+
+        /// <summary>
+        /// Collects all Strokes on RightCanvas
+        /// </summary>
+        public void RightCanvas_StrokeCollection(object sender, InkCanvasStrokeCollectedEventArgs e)
+        {
+            strokeCollection.Add(e.Stroke);
+        }
+
+        /// <summary>
+        /// Redo an Action.
+        /// </summary>
+        private void RedoButton_Click(object sender, RoutedEventArgs e)
+        {
+            if (!IsMousePressed()) ProgramPresenter.Redo();
+        }
+
+        /// <summary>
+        /// Undo an Action.
+        /// </summary>
+        private void UndoButton_Click(object sender, RoutedEventArgs e)
+        {
+            if(!IsMousePressed()) ProgramPresenter.Undo();
+        }
+
+        /// <summary>
+        /// Changes the state of the program to deletion
+        /// </summary>
+        private void DeleteButton_Click(object sender, RoutedEventArgs e)
+        {
+            ProgramPresenter.ChangeState(false);
+            RightCanvas.EditingMode = InkCanvasEditingMode.EraseByStroke;
+        }
+
+        /// <summary>
+        /// Changes the state of the program to drawing
+        /// </summary>
+        private void DrawButton_Click(object sender, RoutedEventArgs e)
+        {
+            ProgramPresenter.ChangeState(true);
+            RightCanvas.EditingMode = InkCanvasEditingMode.Ink;
+        }
+
+        /// <summary>
+        /// Hold left mouse button to start drawing.
+        /// </summary>
+        private void RightCanvas_MouseDown(object sender, MouseButtonEventArgs e)
+        {
+            ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Down, strokeCollection);
+        }
+
+        /// <summary>
+        /// Lift left mouse button to stop drawing and add a new Line.
+        /// </summary>
+        private void RightCanvas_MouseUp(object sender, MouseButtonEventArgs e)
+        {
+            if(strokeCollection.Count == 0)
+            {
+                ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up_Invalid, strokeCollection);
+            }
+            else
+            {
+                ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up, strokeCollection);
+                RightCanvas.Strokes.RemoveAt(0);
+                strokeCollection.RemoveAt(0);
+            }
+        }
+
+        /// <summary>
+        /// Is called when a stylus is lifted, which has the same effect as releasing the mouse.
+        /// Lifting the finger when using touch also toggles this, therfore this function is sufficient.
+        /// </summary>
+        private void RightCanvas_IsStylusCapturedChanged(object sender, DependencyPropertyChangedEventArgs e)
+        {
+            System.Diagnostics.Debug.WriteLine("Stylus Capture is now: {0}", RightCanvas.IsStylusCaptured);
+            if (!RightCanvas.IsStylusCaptured)
+            {
+                if (strokeCollection.Count == 0)
+                {
+                    ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up_Invalid,strokeCollection);
+                }
+                else
+                {
+                    ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up, strokeCollection);
+                    RightCanvas.Strokes.RemoveAt(0);
+                    strokeCollection.RemoveAt(0);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Get current Mouse positon within the right picture box.
+        /// </summary>
+        private void RightCanvas_MouseMove(object sender, MouseEventArgs e)
+        {
+            ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Move, e.GetPosition(RightCanvas));
+        }
+
+        /// <summary>
+        /// Button to create a new Canvas. Will create an empty image 
+        /// which is the size of the left image, if there is one.
+        /// If there is no image loaded the canvas will be the size of the right picture box
+        /// </summary>
+        private void CanvasButton_Click(object sender, RoutedEventArgs e)
+        {
+            ProgramPresenter.NewCanvas();
+            RightCanvas.EditingMode = InkCanvasEditingMode.Ink;
+            RightCanvas.Strokes.Clear();
+        }
+
+        /// <summary>
+        /// Ticks the Presenter.
+        /// </summary>
+        private void dispatcherTimer_Tick(object sender, EventArgs e)
+        {
+            ProgramPresenter.Tick();
+        }
+
+        /// <summary>
+        /// Import button for .svg file, will open an OpenFileDialog
+        /// </summary>
+        private void SVGMenuItem_Click(object sender, RoutedEventArgs e)
+        {
+            if(ProgramPresenter.SVGToolStripMenuItemClick())
+                RightCanvas.EditingMode = InkCanvasEditingMode.Ink;
+        }
+
+        /*************************/
+        /*** PRESENTER -> VIEW ***/
+        /*************************/
+
+        /// <summary>
+        /// Returns the cursor position.
+        /// </summary>
+        /// <returns>The cursor Position</returns>
+        public Point GetCursorPosition()
+        {
+            return Mouse.GetPosition(RightCanvas);
+        }
+
+        /// <summary>
+        /// If the mouse is pressed or not.
+        /// </summary>
+        /// <returns>Whether or not the mouse is pressed.</returns>
+        public bool IsMousePressed()
+        {
+            if (!debugRunning) {
+                return (Mouse.LeftButton.Equals(MouseButtonState.Pressed) || Mouse.RightButton.Equals(MouseButtonState.Pressed)); }
+            else return true;
+        }
+
+        /// <summary>
+        /// Remove the current line.
+        /// </summary>
+        public void RemoveCurrLine()
+        {
+            RightCanvas.Children.Remove(currentLine);
+        }
+
+        /// <summary>
+        /// Display the current line.
+        /// </summary>
+        /// <param name="line">The current line to display</param>
+        public void DisplayCurrLine(Polyline line)
+        {
+            if (RightCanvas.Children.Contains(currentLine))
+            {
+                RemoveCurrLine();
+            }
+            RightCanvas.Children.Add(line);
+            currentLine = line;
+        }
+
+        /// <summary>
+        /// Removes all Lines from the left canvas.
+        /// </summary>
+        public void RemoveAllLeftLines()
+        {
+            LeftCanvas.Children.Clear();
+        }
+
+        /// <summary>
+        /// Removes all lines in the right canvas.
+        /// </summary>
+        public void RemoveAllRightLines()
+        {
+            RightCanvas.Children.Clear();
+        }
+
+        /// <summary>
+        /// Adds another Line that will be displayed in the left display.
+        /// </summary>
+        /// <param name="newLine">The new Polyline to be added displayed.</param>
+        public void AddNewLineLeft(Polyline newLine)
+        {
+            newLine.Stroke = Brushes.Black;
+            newLine.StrokeThickness = 2;
+            LeftCanvas.Children.Add(newLine);
+        }
+
+        /// <summary>
+        /// Adds another Line that will be displayed in the right display.
+        /// </summary>
+        /// <param name="newLine">The new Polyline to be added displayed.</param>
+        public void AddNewLineRight(Polyline newLine)
+        {
+            newLine.Stroke = Brushes.Black;
+            newLine.StrokeThickness = 2;
+            RightCanvas.Children.Add(newLine);
+        }
+
+        /// <summary>
+        /// Adds a point to the right canvas
+        /// </summary>
+        /// <param name="newPoint">The point</param>
+        public void AddNewPointRight(Ellipse newPoint, InternalLine line)
+        {
+            newPoint.Height = 3; newPoint.Width = 3;
+            newPoint.Fill = Brushes.Black;
+            RightCanvas.Children.Add(newPoint);
+            newPoint.Margin = new Thickness(line.point.X - 1.5, line.point.Y - 1.5, 0,0);
+        }
+
+        /// <summary>
+        /// Adds a point to the left canvas
+        /// </summary>
+        /// <param name="newPoint">The point</param>
+        public void AddNewPointLeft(Ellipse newPoint)
+        {
+            newPoint.Height = 3; newPoint.Width = 3;
+            newPoint.Fill = Brushes.Black;
+            LeftCanvas.Children.Add(newPoint);
+        }
+
+        /// <summary>
+        /// Enables the timer of the View, which will tick the Presenter.
+        /// </summary>
+        public void EnableTimer()
+        {
+            dispatcherTimer.Start();
+        }
+
+        /// <summary>
+        /// A function that opens a file dialog and returns the filename.
+        /// </summary>
+        /// <param name="Filter">The filter that should be applied to the new Dialog.</param>
+        /// <returns>Returns the FileName and the SafeFileName if the user correctly selects a file, 
+        /// else returns a tuple with empty strigns</returns>
+        public Tuple<string, string> openNewDialog(string Filter)
+        {
+            openFileDialog.Filter = Filter;
+            if (openFileDialog.ShowDialog() == true)
+            {
+                return new Tuple<string, string>(openFileDialog.FileName, openFileDialog.SafeFileName);
+            }
+            else
+            {
+                return new Tuple<string, string>("", "");
+            }
+        }
+
+        /// <summary>
+        /// Sets the contents of the last action taken indicator label.
+        /// </summary>
+        /// <param name="message">The new contents</param>
+        public void SetLastActionTakenText(string message)
+        {
+            LastActionBox.Text = message;
+        }
+
+        /// <summary>
+        /// Sets the contents of the status bar label containing
+        /// the similarity score of the left and right image.
+        /// </summary>
+        /// <param name="message">The message to be set, 
+        /// will be set to the default value if left empty.</param>
+        public void SetImageSimilarityText(string message)
+        {
+            if (message.Count() > 0) LineSimilarityBox.Text = message;
+            else LineSimilarityBox.Text = "-";
+        }
+
+        /// <summary>
+        /// Changes the states of a tool strip button.
+        /// </summary>
+        /// <param name="buttonName">The name of the button.</param>
+        /// <param name="state">The new state of the button.</param>
+        public void SetToolStripButtonStatus(string buttonName, MainWindow.ButtonState state)
+        {
+            ButtonBase buttonToChange;
+            bool isToggleable = false;
+            switch (buttonName)
+            {
+                case "canvasButton":
+                    buttonToChange = CanvasButton;
+                    break;
+                case "drawButton":
+                    buttonToChange = DrawButton;
+                    isToggleable = true;
+                    break;
+                case "deleteButton":
+                    buttonToChange = DeleteButton;
+                    isToggleable = true;
+                    break;
+                case "undoButton":
+                    buttonToChange = UndoButton;
+                    break;
+                case "redoButton":
+                    buttonToChange = RedoButton;
+                    break;
+                default:
+                    Console.WriteLine("Invalid Button was given to SetToolStripButton. \nMaybe you forgot to add a case?");
+                    return;
+            }
+            if (isToggleable)
+            {
+                switch (state)
+                {
+                    case ButtonState.Active:
+                        ((ToggleButton)buttonToChange).IsEnabled = true;
+                        ((ToggleButton)buttonToChange).IsChecked = true;
+                        ((ToggleButton)buttonToChange).Opacity = 1;
+                        ((ToggleButton)buttonToChange).Background = Brushes.SkyBlue;
+                        break;
+                    case ButtonState.Disabled:
+                        ((ToggleButton)buttonToChange).IsEnabled = false;
+                        ((ToggleButton)buttonToChange).IsChecked = false;
+                        ((ToggleButton)buttonToChange).Opacity = 0.5;
+                        ((ToggleButton)buttonToChange).Background = Brushes.LightGray;
+                        break;
+                    case ButtonState.Enabled:
+                        ((ToggleButton)buttonToChange).IsEnabled = true;
+                        ((ToggleButton)buttonToChange).IsChecked = false;
+                        ((ToggleButton)buttonToChange).Opacity = 1;
+                        ((ToggleButton)buttonToChange).Background = Brushes.LightGray;
+                        break;
+                }
+            }
+            else
+            {
+                switch (state)
+                {
+                    case ButtonState.Disabled:
+                        ((Button)buttonToChange).IsEnabled = false;
+                        ((Button)buttonToChange).Opacity = 0.5;
+                        break;
+                    default:
+                        ((Button)buttonToChange).IsEnabled = true;
+                        ((Button)buttonToChange).Opacity = 1;
+                        break;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Sets the contents of the load status indicator label.
+        /// </summary>
+        /// <param name="message">The new contents</param>
+        public void SetToolStripLoadStatus(string message)
+        {
+            LoadStatusBox.Text = message;
+        }
+
+        /// <summary>
+        /// shows the given info message in a popup and asks the user to aknowledge it
+        /// </summary>
+        /// <param name="message">the message to show</param>
+        public void ShowInfoMessage(string message)
+        {
+            MessageBox.Show(message);
+        }
+
+        /// <summary>
+        /// Shows a warning box with the given message (Yes/No Buttons)and returns true if the user aknowledges it.
+        /// </summary>
+        /// <param name="message">The message of the warning.</param>
+        /// <returns>True if the user confirms (Yes), negative if he doesn't (No)</returns>
+        public bool ShowWarning(string message)
+        {
+            MessageBoxResult result = MessageBox.Show(message, "Warning", MessageBoxButton.YesNo, MessageBoxImage.Warning);
+            return (result.Equals(MessageBoxResult.Yes));
+        }
+
+        /// <summary>
+        /// Updates the colour of a canvas.
+        /// </summary>
+        /// <param name="canvasName">The name of the canvas to be updated.</param>
+        /// <param name="active">Whether or not the canvas is active.</param>
+        public void SetCanvasState(string canvasName, bool active)
+        {
+            switch (canvasName)
+            {
+                case ("LeftCanvas"):
+                    if (active)
+                    {
+                        LeftCanvas.Background = Brushes.White;
+                    }
+                    else
+                    {
+                        LeftCanvas.Background = Brushes.SlateGray;
+                    }
+                    break;
+                case ("RightCanvas"):
+                    if (active)
+                    {
+                        RightCanvas.Background = Brushes.White;
+                    }
+                    else
+                    {
+                        RightCanvas.Background = Brushes.SlateGray;
+                    }
+                    break;
+                default:
+                    throw new InvalidOperationException("Unknown canvas name, Check that the canvas passed is either LeftCanvas or RightCanvas");
+            }
+        }
+
+        /************************/
+        /*** HELPING FUNCTION ***/
+        /************************/
+
+        /// <summary>
+        /// A function that generates the overlay elements and sets all their values.
+        /// </summary>
+        private void SetupOverlay()
+        {
+            DropShadowEffect effect = new DropShadowEffect(); effect.ShadowDepth = 0;
+            OverlayCanvas.Background = null;
+            //Startpoint of a line to be redrawn
+            Ellipse StartPointOverlay = new Ellipse();
+            StartPointOverlay.Height = markerRadius * 2; StartPointOverlay.Width = markerRadius * 2;
+            StartPointOverlay.Fill = Brushes.Green;
+            StartPointOverlay.Effect = effect;
+            OverlayDictionary.Add("startpoint", StartPointOverlay);
+            //Endpoint of a line to be redrawn
+            Ellipse EndPointOverlay = new Ellipse();
+            EndPointOverlay.Height = markerRadius * 2; StartPointOverlay.Width = markerRadius * 2;
+            EndPointOverlay.Fill = Brushes.Green;
+            EndPointOverlay.Effect = effect;
+            OverlayDictionary.Add("endpoint", StartPointOverlay);
+            //Pointer of the optitrack system
+            Ellipse OptitrackMarker = new Ellipse(); OptitrackMarker.Height = 5; OptitrackMarker.Width = 5;
+            OptitrackMarker.Fill = Brushes.LightGray;
+            OptitrackMarker.Effect = effect;
+            OverlayDictionary.Add("optipoint", OptitrackMarker);
+            //10 Dotted Lines for debugging (if more are needed simply extend the for-loop
+            for(int x = 0; x < 10; x++)
+            {
+                Line dotLine = new Line();
+                dotLine.Stroke = Brushes.Red;
+                dotLine.StrokeDashArray = new DoubleCollection { 2 + x, 2 + x };
+                dotLine.StrokeThickness = 1;
+                OverlayDictionary.Add("dotLine" + x.ToString(), dotLine);
+            }
+
+            //Common features of all overlay items
+            foreach (KeyValuePair<String, Shape> s  in OverlayDictionary)
+            {
+                OverlayCanvas.Children.Add(s.Value);
+                s.Value.Opacity = 0.00001;
+                s.Value.IsHitTestVisible = false;
+            }
+
+            //Enable optipoint initially
+            ProgramPresenter.SetOverlayStatus("optipoint", true, GetCursorPosition());
+        }
+
+        /// <summary>
+        /// Sends inputs to the presenter simulating drawing, used for testing and debugging.
+        /// Takes 7000ms
+        /// </summary>
+        private void DebugOne_Click(object sender, RoutedEventArgs e)
+        {
+            Debug(1);
+        }
+
+        /// <summary>
+        /// Sends inputs to the presenter simulating drawing, used for testing and debugging.
+        /// Takes 24000ms
+        /// </summary>
+        private void DebugTwo_Click(object sender, RoutedEventArgs e)
+        {
+            Debug(2);
+        }
+
+        /// <summary>
+        /// Sends inputs to the presenter simulating drawing, used for testing and debugging.
+        /// Takes 4000ms
+        /// </summary>
+        private void DebugThree_Click(object sender, RoutedEventArgs e)
+        {
+            Debug(3);
+        }
+
+        /// <summary>
+        /// Sends inputs to the presenter simulating drawing, used for testing and debugging.
+        /// Takes 
+        /// </summary>
+        private void DebugFour_Click(object sender, RoutedEventArgs e)
+        {
+            Debug(4);
+        }
+
+        /// <summary>
+        /// A function which simulates canvas input for debugging.
+        /// </summary>
+        /// <param name="option"></param>
+        private async void Debug(int option)
+        {
+            Point[] points;
+            Point start = new Point(50, 50);
+            switch (option)
+            {
+                case 1:
+                    points = debugDat.debugPoints1;
+                    break;
+                case 2:
+                    points = debugDat.debugPoints2;
+                    break;
+                case 3:
+                    points = debugDat.debugPoints3;
+                    break;
+                case 4:
+                    points = debugDat.debugPoints4;
+                    start = new Point(284, 148);
+                    break;
+                default:
+                    return;
+            }
+            dispatcherTimer.Stop();
+            debugRunning = true;
+            ProgramPresenter.Tick(); await Task.Delay(10);
+            ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Move, start);
+            ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Down, strokeCollection); await Task.Delay(10);
+            for (int x = 0; x < points.Length; x++)
+            {
+                ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Move, points[x]);
+                await Task.Delay(1);
+                if (x % 5 == 0)
+                {
+                    ProgramPresenter.Tick();
+                    await Task.Delay(1);
+                }
+            }
+            ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up, strokeCollection); await Task.Delay(1);
+            debugRunning = false;
+            dispatcherTimer.Start();
+        }
+    }
+}

+ 55 - 0
SketchAssistant/SketchAssistantWPF/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("SketchAssistantWPF")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("SketchAssistantWPF")]
+[assembly: AssemblyCopyright("Copyright ©  2019")]
+[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
+//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
+//inside a <PropertyGroup>.  For example, if you are using US english
+//in your source files, set the <UICulture> 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")]

+ 20 - 28
SketchAssistant/SketchAssistant/Properties/Resources.Designer.cs → SketchAssistant/SketchAssistantWPF/Properties/Resources.Designer.cs

@@ -1,4 +1,4 @@
-//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 // <auto-generated>
 //     This code was generated by a tool.
 //     Runtime Version:4.0.30319.42000
@@ -8,10 +8,10 @@
 // </auto-generated>
 //------------------------------------------------------------------------------
 
-namespace SketchAssistant.Properties
-{
-
-
+namespace SketchAssistantWPF.Properties {
+    using System;
+    
+    
     /// <summary>
     ///   A strongly-typed resource class, for looking up localized strings, etc.
     /// </summary>
@@ -19,51 +19,43 @@ namespace SketchAssistant.Properties
     // 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.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
     [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
-    internal class Resources
-    {
-
+    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()
-        {
+        internal Resources() {
         }
-
+        
         /// <summary>
         ///   Returns the cached ResourceManager instance used by this class.
         /// </summary>
         [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("SketchAssistant.Properties.Resources", typeof(Resources).Assembly);
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SketchAssistantWPF.Properties.Resources", typeof(Resources).Assembly);
                     resourceMan = temp;
                 }
                 return resourceMan;
             }
         }
-
+        
         /// <summary>
         ///   Overrides the current thread's CurrentUICulture property for all
         ///   resource lookups using this strongly typed resource class.
         /// </summary>
         [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
-        internal static global::System.Globalization.CultureInfo Culture
-        {
-            get
-            {
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
                 return resourceCulture;
             }
-            set
-            {
+            set {
                 resourceCulture = value;
             }
         }

+ 10 - 7
SketchAssistant/SketchAssistant/Properties/Resources.resx → SketchAssistant/SketchAssistantWPF/Properties/Resources.resx

@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <root>
   <!-- 
     Microsoft ResX Schema 
@@ -46,7 +46,7 @@
     
     mimetype: application/x-microsoft.net.object.binary.base64
     value   : The object must be serialized with 
-            : System.Serialization.Formatters.Binary.BinaryFormatter
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
             : and then encoded with base64 encoding.
     
     mimetype: application/x-microsoft.net.object.soap.base64
@@ -60,6 +60,7 @@
             : and then encoded with base64 encoding.
     -->
   <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
     <xsd:element name="root" msdata:IsDataSet="true">
       <xsd:complexType>
         <xsd:choice maxOccurs="unbounded">
@@ -68,9 +69,10 @@
               <xsd:sequence>
                 <xsd:element name="value" type="xsd:string" minOccurs="0" />
               </xsd:sequence>
-              <xsd:attribute name="name" type="xsd:string" />
+              <xsd:attribute name="name" use="required" type="xsd:string" />
               <xsd:attribute name="type" type="xsd:string" />
               <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
             </xsd:complexType>
           </xsd:element>
           <xsd:element name="assembly">
@@ -85,9 +87,10 @@
                 <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                 <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
               </xsd:sequence>
-              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
               <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
               <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
             </xsd:complexType>
           </xsd:element>
           <xsd:element name="resheader">
@@ -109,9 +112,9 @@
     <value>2.0</value>
   </resheader>
   <resheader name="reader">
-    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
   <resheader name="writer">
-    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
-</root>
+</root>

+ 2 - 2
SketchAssistant/SketchAssistant/Properties/Settings.Designer.cs → SketchAssistant/SketchAssistantWPF/Properties/Settings.Designer.cs

@@ -1,4 +1,4 @@
-//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
 // <auto-generated>
 //     This code was generated by a tool.
 //     Runtime Version:4.0.30319.42000
@@ -8,7 +8,7 @@
 // </auto-generated>
 //------------------------------------------------------------------------------
 
-namespace SketchAssistant.Properties
+namespace SketchAssistantWPF.Properties
 {
 
 

+ 7 - 0
SketchAssistant/SketchAssistantWPF/Properties/Settings.settings

@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
+  <Profiles>
+    <Profile Name="(Default)" />
+  </Profiles>
+  <Settings />
+</SettingsFile>

+ 2 - 5
SketchAssistant/SketchAssistant/SketchAction.cs → SketchAssistant/SketchAssistantWPF/SketchAction.cs

@@ -4,7 +4,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace SketchAssistant
+namespace SketchAssistantWPF
 {
     public class SketchAction
     {
@@ -72,7 +72,7 @@ namespace SketchAssistant
         /// <returns>A String describing what happend at this action.</returns>
         public String GetActionInformation()
         {
-            String returnString;
+            String returnString = "";
             switch (thisAction)
             {
                 case ActionType.Start:
@@ -88,9 +88,6 @@ namespace SketchAssistant
                         returnString = "Several Lines were deleted.";
                     }
                     break;
-                default:
-                    returnString = "There is no information available for this action.";
-                    break;
             }
             return returnString;
         }

+ 69 - 44
SketchAssistant/SketchAssistant/SketchAssistant.csproj → SketchAssistant/SketchAssistantWPF/SketchAssistantWPF.csproj

@@ -1,16 +1,23 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="..\packages\ReportGenerator.4.0.15\build\netstandard2.0\ReportGenerator.props" Condition="Exists('..\packages\ReportGenerator.4.0.15\build\netstandard2.0\ReportGenerator.props')" />
   <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProjectGuid>{0336F628-A2F7-4170-8B2E-9277C23118D4}</ProjectGuid>
+    <ProjectGuid>{EE53AE79-2AA0-4F43-9638-1789B189D5C3}</ProjectGuid>
     <OutputType>WinExe</OutputType>
-    <RootNamespace>SketchAssistant</RootNamespace>
-    <AssemblyName>SketchAssistant</AssemblyName>
+    <RootNamespace>SketchAssistantWPF</RootNamespace>
+    <AssemblyName>SketchAssistantWPF</AssemblyName>
     <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
     <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <WarningLevel>4</WarningLevel>
     <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+    <Deterministic>true</Deterministic>
+    <NuGetPackageImportStamp>
+    </NuGetPackageImportStamp>
+    <IsWebBootstrapper>false</IsWebBootstrapper>
     <PublishUrl>publish\</PublishUrl>
     <Install>true</Install>
     <InstallFrom>Disk</InstallFrom>
@@ -23,11 +30,8 @@
     <MapFileExtensions>true</MapFileExtensions>
     <ApplicationRevision>0</ApplicationRevision>
     <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
-    <IsWebBootstrapper>false</IsWebBootstrapper>
     <UseApplicationTrust>false</UseApplicationTrust>
     <BootstrapperEnabled>true</BootstrapperEnabled>
-    <NuGetPackageImportStamp>
-    </NuGetPackageImportStamp>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <PlatformTarget>AnyCPU</PlatformTarget>
@@ -48,69 +52,81 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
+  <PropertyGroup>
+    <ApplicationIcon>nicubunu-Quill.ico</ApplicationIcon>
+  </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
-    <Reference Include="System.Configuration" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Xml" />
+    <Reference Include="Microsoft.CSharp" />
     <Reference Include="System.Core" />
-    <Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
-      <HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
-    </Reference>
-    <Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
-      <HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.1\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll</HintPath>
-    </Reference>
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
-    <Reference Include="Microsoft.CSharp" />
-    <Reference Include="System.Data" />
-    <Reference Include="System.Deployment" />
-    <Reference Include="System.Drawing" />
     <Reference Include="System.Net.Http" />
-    <Reference Include="System.Windows.Forms" />
-    <Reference Include="System.Xml" />
+    <Reference Include="System.Xaml">
+      <RequiredTargetFramework>4.0</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="WindowsBase" />
+    <Reference Include="PresentationCore" />
+    <Reference Include="PresentationFramework" />
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="RedrawAssistant.cs" />
+    <ApplicationDefinition Include="App.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </ApplicationDefinition>
+    <Compile Include="ActionHistory.cs" />
+    <Compile Include="DebugData.cs" />
     <Compile Include="FileImporter.cs" />
     <Compile Include="FileImporterException.cs" />
     <Compile Include="GeometryCalculator.cs" />
+    <Compile Include="ImageDimension.cs" />
     <Compile Include="SketchAction.cs" />
-    <Compile Include="ActionHistory.cs" />
-    <Compile Include="Line.cs" />
-    <Compile Include="Form1.cs">
-      <SubType>Form</SubType>
+    <Page Include="MainWindow.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Compile Include="App.xaml.cs">
+      <DependentUpon>App.xaml</DependentUpon>
+      <SubType>Code</SubType>
     </Compile>
-    <Compile Include="Form1.Designer.cs">
-      <DependentUpon>Form1.cs</DependentUpon>
+    <Compile Include="InternalLine.cs" />
+    <Compile Include="MainWindow.xaml.cs">
+      <DependentUpon>MainWindow.xaml</DependentUpon>
+      <SubType>Code</SubType>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="MVP_Model.cs" />
+    <Compile Include="MVP_Presenter.cs" />
+    <Compile Include="MVP_View.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs">
+      <SubType>Code</SubType>
     </Compile>
-    <Compile Include="Program.cs" />
-    <Compile Include="Properties\AssemblyInfo.cs" />
-    <EmbeddedResource Include="Form1.resx">
-      <DependentUpon>Form1.cs</DependentUpon>
-    </EmbeddedResource>
-    <EmbeddedResource Include="Properties\Resources.resx">
-      <Generator>ResXFileCodeGenerator</Generator>
-      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
-      <SubType>Designer</SubType>
-    </EmbeddedResource>
     <Compile Include="Properties\Resources.Designer.cs">
       <AutoGen>True</AutoGen>
+      <DesignTime>True</DesignTime>
       <DependentUpon>Resources.resx</DependentUpon>
     </Compile>
-    <None Include="packages.config" />
-    <None Include="Properties\Settings.settings">
-      <Generator>SettingsSingleFileGenerator</Generator>
-      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
-    </None>
     <Compile Include="Properties\Settings.Designer.cs">
       <AutoGen>True</AutoGen>
       <DependentUpon>Settings.settings</DependentUpon>
       <DesignTimeSharedInput>True</DesignTimeSharedInput>
     </Compile>
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+    </EmbeddedResource>
+    <None Include="packages.config" />
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
   </ItemGroup>
   <ItemGroup>
-    <None Include="App.config">
-      <SubType>Designer</SubType>
-    </None>
+    <None Include="App.config" />
   </ItemGroup>
   <ItemGroup>
     <BootstrapperPackage Include=".NETFramework,Version=v4.6.1">
@@ -124,5 +140,14 @@
       <Install>false</Install>
     </BootstrapperPackage>
   </ItemGroup>
+  <ItemGroup>
+    <Resource Include="nicubunu-Quill.ico" />
+  </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('..\packages\ReportGenerator.4.0.15\build\netstandard2.0\ReportGenerator.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\ReportGenerator.4.0.15\build\netstandard2.0\ReportGenerator.props'))" />
+  </Target>
 </Project>

BIN
SketchAssistant/SketchAssistantWPF/nicubunu-Quill.ico


+ 6 - 0
SketchAssistant/SketchAssistantWPF/packages.config

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="Microsoft.TestPlatform" version="16.0.1" targetFramework="net461" />
+  <package id="OpenCover" version="4.7.922" targetFramework="net461" />
+  <package id="ReportGenerator" version="4.0.15" targetFramework="net461" />
+</packages>

+ 4 - 4
SketchAssistant/SketchAssistant.Tests/Properties/AssemblyInfo.cs → SketchAssistant/WhiteTests/Properties/AssemblyInfo.cs

@@ -2,18 +2,18 @@ using System.Reflection;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 
-[assembly: AssemblyTitle("SketchAssistant.Tests")]
+[assembly: AssemblyTitle("WhiteTests")]
 [assembly: AssemblyDescription("")]
 [assembly: AssemblyConfiguration("")]
 [assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("SketchAssistant.Tests")]
-[assembly: AssemblyCopyright("Copyright ©  2018")]
+[assembly: AssemblyProduct("WhiteTests")]
+[assembly: AssemblyCopyright("Copyright ©  2019")]
 [assembly: AssemblyTrademark("")]
 [assembly: AssemblyCulture("")]
 
 [assembly: ComVisible(false)]
 
-[assembly: Guid("7dcdc31a-8291-4b05-93d6-dcc5de27a4a0")]
+[assembly: Guid("eb09c624-91f2-465f-825b-559bf7a7d5cb")]
 
 // [assembly: AssemblyVersion("1.0.*")]
 [assembly: AssemblyVersion("1.0.0.0")]

+ 1012 - 0
SketchAssistant/WhiteTests/UITest.cs

@@ -0,0 +1,1012 @@
+using System;
+using System.IO;
+using System.Reflection;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using TestStack.White;
+using TestStack.White.UIItems;
+using TestStack.White.UIItems.WindowItems;
+using TestStack.White.UIItems.Finders;
+using System.Threading;
+using SketchAssistantWPF;
+using System.Windows;
+using System.Diagnostics;
+using TestStack.White.UIItems.WindowStripControls;
+using TestStack.White.UIItems.MenuItems;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using WindowsInput;
+using WindowsInput.Native;
+using System.Threading.Tasks;
+using System.Linq;
+using Application = TestStack.White.Application;
+using Window = TestStack.White.UIItems.WindowItems.Window;
+
+namespace WhiteTests
+{
+
+    [TestClass]
+    public class UITest
+    {
+        private TestStack.White.Application application;
+
+        /// <summary>
+        /// The directory of the input files, saved for repeated use
+        /// </summary>
+        private String input_file_dir = null;
+        /// <summary>
+        /// instance of TestContext to be able to access deployed files
+        /// </summary>
+        private TestContext testContextInstance;
+        /// <summary>
+        ///Gets or sets the test context which provides
+        ///information about and functionality for the current test run.
+        ///</summary>
+        public TestContext TestContext
+        {
+            get
+            {
+                return testContextInstance;
+            }
+            set
+            {
+                testContextInstance = value;
+            }
+        }
+
+        /// <summary>
+        /// A function that returns the path to the test_input_files folder. 
+        /// Do with it what you want.
+        /// </summary>
+        /// <returns>the path to the test_input_files folder</returns>
+        public String getSketchAssistantDirectory()
+        {
+            Regex rx = new Regex(@"^(.*\\SketchAssistant\\)");
+            Match match = rx.Match(TestContext.DeploymentDirectory);
+            String SketchAssistDir = match.Groups[1].Value;
+            if (input_file_dir == null)
+            {
+                if (Directory.Exists(SketchAssistDir + @"WhiteTests\test_input_files\"))
+                {
+                    input_file_dir = SketchAssistDir + @"WhiteTests\test_input_files\";
+                }
+                else if (Directory.Exists(SketchAssistDir + @"WhiteTests\bin\Debug\test_input_files\"))
+                {
+                    input_file_dir = SketchAssistDir + @"WhiteTests\bin\Debug\test_input_files\";
+                }
+                else
+                {
+                    Regex rx_0 = new Regex(@"^(.*\\projects\\)");
+                    Match match_0 = rx_0.Match(TestContext.DeploymentDirectory);
+                    String ProjectsDir = match_0.Groups[1].Value;
+                    var dirs = Directory.GetDirectories(ProjectsDir, "test_input_files", SearchOption.AllDirectories);
+                    input_file_dir = dirs[0];
+                }
+            }
+            return input_file_dir;
+        }
+
+        public Window setupapp()
+        {
+            string outputDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+            string editedDir = outputDir.Replace("WhiteTests", "SketchAssistantWPF");
+            string app_path = editedDir + @"\SketchAssistantWPF.exe";
+            ProcessStartInfo processStart = new ProcessStartInfo(app_path, "-debug");
+            application = Application.Launch(processStart);
+            return application.GetWindow("Sketch Assistant");
+        }
+
+        [DataTestMethod]
+        [TestCategory("FileIO")]
+        [DataRow("line")]
+        public void LoadSVGFileTest(String filename)
+        {
+            Window mainWindow = setupapp();
+            InputSimulator inputSimulator = new InputSimulator();
+            Thread.Sleep(20);
+            string[] files = Directory.GetFiles(getSketchAssistantDirectory() + @"\whitelisted", "*.svg", SearchOption.AllDirectories);
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("LoadMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("SVGMenuButton")).Click();
+            Thread.Sleep(1000);
+            inputSimulator.Keyboard.TextEntry(getSketchAssistantDirectory() + @"whitelisted\" + filename + ".svg");
+            Thread.Sleep(1000);
+            inputSimulator.Keyboard.KeyPress(VirtualKeyCode.RETURN);
+            Thread.Sleep(1000);
+            //Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DirectInput")]
+        public void DrawLineOnCanvasTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            InputSimulator inputSimulator = new InputSimulator();
+            MouseSimulator mouseSimulator = new MouseSimulator(inputSimulator);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            inputSimulator.Mouse.MoveMouseBy(100,100);
+            inputSimulator.Mouse.LeftButtonDown();
+            Thread.Sleep(20);
+            inputSimulator.Mouse.MoveMouseBy(100, 100);
+            Thread.Sleep(20);
+            inputSimulator.Mouse.LeftButtonUp();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DirectInput")]
+        public void UndoLineOnCanvasTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            InputSimulator inputSimulator = new InputSimulator();
+            MouseSimulator mouseSimulator = new MouseSimulator(inputSimulator);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            inputSimulator.Mouse.MoveMouseBy(0, 200);
+            inputSimulator.Mouse.LeftButtonDown();
+            Thread.Sleep(20);
+            inputSimulator.Mouse.MoveMouseBy(500, 300);
+            Thread.Sleep(20);
+            inputSimulator.Mouse.LeftButtonUp();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(100);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DirectInput")]
+        public void InvalidLineTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            InputSimulator inputSimulator = new InputSimulator();
+            MouseSimulator mouseSimulator = new MouseSimulator(inputSimulator);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("DrawButton")).Click();
+            Thread.Sleep(20);
+            inputSimulator.Mouse.LeftButtonDown();
+            inputSimulator.Mouse.MoveMouseBy(0, 200);
+            Thread.Sleep(20);
+            inputSimulator.Mouse.MoveMouseBy(500, 300);
+            Thread.Sleep(20);
+            inputSimulator.Mouse.LeftButtonUp();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            inputSimulator.Mouse.MoveMouseBy(-1000, 0);
+            Thread.Sleep(20);
+            inputSimulator.Mouse.LeftButtonDown();
+            inputSimulator.Mouse.MoveMouseBy(1000, 0);
+            inputSimulator.Mouse.LeftButtonUp();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DirectInput")]
+        public void PointsOnCanvasSimilarityTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            InputSimulator inputSimulator = new InputSimulator();
+            MouseSimulator mouseSimulator = new MouseSimulator(inputSimulator);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Assert.AreEqual("-", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LineSimilarityBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            inputSimulator.Mouse.MoveMouseBy(0, 200);
+            inputSimulator.Mouse.LeftButtonDown();
+            Thread.Sleep(20);
+            inputSimulator.Mouse.LeftButtonUp();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Assert.AreEqual("-", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LineSimilarityBox")).Text.ToString());
+            Thread.Sleep(20);
+            inputSimulator.Mouse.LeftButtonDown();
+            Thread.Sleep(20);
+            inputSimulator.Mouse.LeftButtonUp();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Assert.AreEqual("1", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LineSimilarityBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Assert.AreEqual("-", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LineSimilarityBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("RedoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Assert.AreEqual("1", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LineSimilarityBox")).Text.ToString());
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DebugInput")]
+        public void CreateCanvasTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DebugInput")]
+        public void DrawLineTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugOne")).Click();
+            Thread.Sleep(7000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DebugInput")]
+        public void DeleteLineTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("DrawButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugOne")).Click();
+            Thread.Sleep(7000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("DeleteButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugOne")).Click();
+            Thread.Sleep(7000);
+            Assert.AreEqual("Last Action: Line number 0 was deleted.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DebugInput")]
+        public void UndoTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugOne")).Click();
+            Thread.Sleep(7000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DebugInput")]
+        public void RedoTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugOne")).Click();
+            Thread.Sleep(7000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("RedoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DebugInput")]
+        public void DrawSeveralLinesTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugOne")).Click();
+            Thread.Sleep(7000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugTwo")).Click();
+            Thread.Sleep(30000);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DebugInput")]
+        public void DeleteSeveralLinesTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugOne")).Click();
+            Thread.Sleep(7000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugTwo")).Click();
+            Thread.Sleep(24000);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("DeleteButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugThree")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 1 was deleted.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(10000);
+            Assert.AreEqual("Last Action: Line number 0 was deleted.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DebugInput")]
+        public void UndoSeveralLinesTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugOne")).Click();
+            Thread.Sleep(7000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugTwo")).Click();
+            Thread.Sleep(30000);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DebugInput")]
+        public void RedoSeveralLinesTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugOne")).Click();
+            Thread.Sleep(7000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugTwo")).Click();
+            Thread.Sleep(24000);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("RedoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("RedoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DebugInput")]
+        public void UndoAndRedoTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugOne")).Click();
+            Thread.Sleep(7000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugTwo")).Click();
+            Thread.Sleep(24000);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("RedoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("RedoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("DeleteButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugThree")).Click();
+            Thread.Sleep(7000);
+            Assert.AreEqual("Last Action: Line number 0 was deleted.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 1 was deleted.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DebugInput")]
+        public void UndoAndDrawTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugFour")).Click();
+            Thread.Sleep(2000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugFour")).Click();
+            Thread.Sleep(2000);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DebugInput")]
+        public void DeleteSeveralLinesAtOnceTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugFour")).Click();
+            Thread.Sleep(2000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugFour")).Click();
+            Thread.Sleep(2000);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("DeleteButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugFour")).Click();
+            Thread.Sleep(2000);
+            Assert.AreEqual("Last Action: Several Lines were deleted.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DebugInput")]
+        public void PointDrawTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugFour")).Click();
+            Thread.Sleep(4000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DebugInput")]
+        public void NewCanvasAfterDrawTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugThree")).Click();
+            Thread.Sleep(4000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            // Click on No button in warning
+            Window messageBox0 = mainWindow.MessageBox("Warning");
+            messageBox0.Get<Button>(SearchCriteria.ByText("No")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            // close warning
+            Window messageBox1 = mainWindow.MessageBox("Warning");
+            messageBox1.Close();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            // click yes button on warning
+            Window messageBox2 = mainWindow.MessageBox("Warning");
+            messageBox2.Get<Button>(SearchCriteria.ByText("Yes")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Close();
+        }
+    }
+
+    [TestClass]
+    public class FileImporterTests
+    {
+        /// <summary>
+        /// The directory of the input files, saved for repeated use
+        /// </summary>
+        private String input_file_dir = null;
+        /// <summary>
+        /// instance of TestContext to be able to access deployed files
+        /// </summary>
+        private TestContext testContextInstance;
+        /// <summary>
+        ///Gets or sets the test context which provides
+        ///information about and functionality for the current test run.
+        ///</summary>
+        public TestContext TestContext
+        {
+            get
+            {
+                return testContextInstance;
+            }
+            set
+            {
+                testContextInstance = value;
+            }
+        }
+
+        /// <summary>
+        /// A function that returns the path to the test_input_files folder. 
+        /// Do with it what you want.
+        /// </summary>
+        /// <returns>the path to the test_input_files folder</returns>
+        public String getSketchAssistantDirectory()
+        {
+            Regex rx = new Regex(@"^(.*\\SketchAssistant\\)");
+            Match match = rx.Match(TestContext.DeploymentDirectory);
+            String SketchAssistDir = match.Groups[1].Value;
+            if (input_file_dir == null)
+            {
+                if (Directory.Exists(SketchAssistDir + @"\WhiteTests\test_input_files\"))
+                {
+                    input_file_dir = SketchAssistDir + @"\WhiteTests\test_input_files\";
+                }
+                else if (Directory.Exists(SketchAssistDir + @"\WhiteTests\bin\Debug\test_input_files\"))
+                {
+                    input_file_dir = SketchAssistDir + @"\WhiteTests\bin\Debug\test_input_files\";
+                }
+                else
+                {
+                    Regex rx_0 = new Regex(@"^(.*\\projects\\)");
+                    Match match_0 = rx_0.Match(TestContext.DeploymentDirectory);
+                    String ProjectsDir = match_0.Groups[1].Value;
+                    var dirs = Directory.GetDirectories(ProjectsDir, "test_input_files", SearchOption.AllDirectories);
+                    input_file_dir = dirs[0];
+                }
+            }
+            return input_file_dir;
+        }
+
+        /// <summary>
+        /// creates a valid .isad file from the given sets of coordinates (number divisible by 3) by creating a line for every three consecutive points, parses the file and verifies that all lines and their points have been parsed correctly
+        /// </summary>
+        /// <param name="xCoordinates">an array containing the x coordinates of the points that will be created (length divisible by 3)</param>
+        /// <param name="yCoordinates">an array containing the y coordinates of the points that will be created (length divisible by 3)</param>
+        [DataTestMethod]
+        [TestCategory("FileIO")]
+        [DataRow(new int[] { 54, 43, 57, 11, 145, 34, 113, 299, 0 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 })]
+        [DataRow(new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 })]
+        [DataRow(new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 54, 43, 57, 11, 145, 34, 113, 199, 0 })]
+        public void ParseISADInputSuccessfulTest(int[] xCoordinates, int[] yCoordinates)
+        {
+            FileImporter uut = new FileImporter();
+
+            List<String> file = new List<string>();
+            file.Add("drawing");
+            file.Add("300x200");
+            for (int i = 0; i < xCoordinates.Length - 2; i += 3)
+            {
+                file.Add("line");
+                file.Add(xCoordinates[i] + ";" + yCoordinates[i]);
+                file.Add(xCoordinates[i + 1] + ";" + yCoordinates[i + 1]);
+                file.Add(xCoordinates[i + 2] + ";" + yCoordinates[i + 2]);
+                file.Add("endline");
+            }
+            file.Add("enddrawing");
+
+            Tuple<int, int, List<InternalLine>> values = uut.ParseISADInputForTesting(file.ToArray());
+
+            Assert.AreEqual(xCoordinates.Length / 3, values.Item3.Count);
+            InternalLine[] lines = values.Item3.ToArray();
+            for (int i = 0; i < xCoordinates.Length - 2; i += 3)
+            {
+                Point[] currentLine = lines[i / 3].GetPoints().ToArray();
+                Assert.AreEqual(3, currentLine.Length);
+                for (int j = 0; j < 3; j++)
+                {
+                    Assert.IsTrue(currentLine[j].X == xCoordinates[i + j] && currentLine[j].Y == yCoordinates[i + j]);
+                }
+            }
+        }
+
+        /// <summary>
+        /// parses teh given invalid .isad files and verifies that a FileImporterException is thrown, but no other exception
+        /// </summary>
+        /// <param name="file">the input file represented as an array of lines</param>
+        [DataTestMethod]
+        [TestCategory("FileIO")]
+        [DataRow(new String[] { })]
+        [DataRow(new String[] { "begindrawing", "300x300", "line", "50;50", "100;50", "endline", "enddrawing" })]
+        [DataRow(new String[] { "drawing", "300;300", "line", "50;50", "100;50", "endline", "enddrawing" })]
+        [DataRow(new String[] { "drawing", "30.5x300", "line", "50;50", "100;50", "endline", "enddrawing" })]
+        [DataRow(new String[] { "drawing", "line", "50;50", "100;50", "endline", "enddrawing" })]
+        [DataRow(new String[] { "drawing", "300x300", "beginline", "50;50", "100;50", "endline", "enddrawing" })]
+        [DataRow(new String[] { "drawing", "300x300", "line", "500;50", "100;50", "endline", "enddrawing" })]
+        [DataRow(new String[] { "drawing", "300x300", "line", "50x50", "100;50", "endline", "enddrawing" })]
+        [DataRow(new String[] { "drawing", "300x300", "line", "50", "100", "endline", "enddrawing" })]
+        [DataRow(new String[] { "drawing", "300x300", "line", "50;50", "line", "endline", "enddrawing" })]
+        [DataRow(new String[] { "drawing", "300x300", "line", "50;50", "100;50", "stopline", "enddrawing" })]
+        [DataRow(new String[] { "drawing", "300x300", "line", "50;50", "100;50", "enddrawing" })]
+        [DataRow(new String[] { "drawing", "300x300", "line", "50;50", "100;50", "endline", "endrawing" })]
+        [DataRow(new String[] { "drawing", "300x300", "line", "50;50", "100;50", "endline" })]
+        public void ParseISADInputExceptionTest(String[] file)
+        {
+            bool correctExceptionThrown = false;
+
+            FileImporter uut = new FileImporter();
+
+            try
+            {
+                //try to initialize the left image with an invalid isad drawing
+                Tuple<int, int, List<InternalLine>> values1 = uut.ParseISADInputForTesting(file);
+            }
+            catch (FileImporterException e)
+            {
+                //save the occurence of an exception
+                correctExceptionThrown = true;
+                System.Diagnostics.Debug.WriteLine(e.ToString());
+            }
+            catch (Exception)
+            {
+                //don't set success flag
+            }
+            //check that an exception has been thrown
+            Assert.IsTrue(correctExceptionThrown);
+        }
+
+        /// <summary>
+        /// parses all whitelisted files and ensures no exceptions are thrown (parsing abortion, e.g. due to corrupted input files, are realized by throwing a FileImporterException)
+        /// </summary>
+        [TestMethod]
+        [TestCategory("FileIO")]
+        public void ParseSVGInputNoErrorForWhitelistedFilesTest()
+        {
+            FileImporter uut = new FileImporter();
+            string[] files = Directory.GetFiles(getSketchAssistantDirectory() + @"\whitelisted", "*.svg", SearchOption.AllDirectories);
+
+            Assert.IsTrue(files.Length > 0);
+
+            foreach (string s in files) //parse each of the whitelisted files
+            {
+                Console.WriteLine(s);
+                bool noExceptionThrown = true;
+                try
+                {
+                    uut.ParseSVGInputFile(s, 10000, 10000);
+                }
+                catch (Exception)
+                {
+                    noExceptionThrown = false;
+                }
+                Assert.IsTrue(noExceptionThrown);
+            }
+        }
+
+        /// <summary>
+        /// parses all blacklisted files and ensures an instance of FileIporterException is thrown for each file, but no other exceptions occur
+        /// </summary>
+        [TestMethod]
+        [TestCategory("FileIO")]
+        public void ParseSVGInputNoErrorForBlacklistedFilesTest()
+        {
+            FileImporter uut = new FileImporter();
+
+            string[] files = Directory.GetFiles(getSketchAssistantDirectory() + @"\blacklisted", "*.svg", SearchOption.AllDirectories);
+            Assert.IsTrue(files.Length > 0);
+            foreach (string s in files) //parse each of the blacklisted files
+            {
+                bool correctExceptionThrown = false;
+                try
+                {
+                    uut.ParseSVGInputFile(s, 10000, 10000);
+                }
+                catch (FileImporterException e)
+                {
+                    System.Diagnostics.Debug.WriteLine(e.ToString());
+                    correctExceptionThrown = true;
+                }
+                catch (Exception)
+                {
+                }
+                Assert.IsTrue(correctExceptionThrown);
+            }
+        }
+    }
+
+    [TestClass]
+    public class SimilarityCalculationTests
+    {
+        /// <summary>
+        /// The debug data element used to generate random lines.
+        /// </summary>
+        private DebugData DebugData = new DebugData();
+
+        /// <summary>
+        /// Generates random lines and tests how similar they are. 
+        /// To test the similarity score always stays between 0 and 1.
+        /// </summary>
+        [TestMethod]
+        [TestCategory("UnitTest")]
+        public void StaysWithinParameters()
+        {
+            Parallel.For(1, 100,
+                i =>
+                {
+                    InternalLine l0 = DebugData.GetRandomLine(1, (uint)i);
+                    InternalLine l1 = DebugData.GetRandomLine(1, (uint)i);
+                    var sim = GeometryCalculator.CalculateSimilarity(l0, l1);
+                    Assert.IsTrue((sim >= 0));
+                    Assert.IsTrue((sim <= 1));
+                });
+        }
+
+        [TestMethod]
+        [TestCategory("UnitTest")]
+        public void CorrectSimilarity()
+        {
+            Parallel.ForEach(DebugData.GetSimilarityTestData(),
+                tup =>
+                {
+                    InternalLine l0 = tup.Item1;
+                    InternalLine l1 = tup.Item2;
+                    var sim = GeometryCalculator.CalculateSimilarity(l0, l1);
+                    Assert.AreEqual(tup.Item3, sim, 0.00000001);
+                });
+        }
+
+    }
+
+    [TestClass]
+    public class InternalLineUnitTests
+    {
+        /// <summary>
+        /// The debug data element used to generate random lines.
+        /// </summary>
+        private DebugData DebugData = new DebugData();
+
+        [TestMethod]
+        [TestCategory("UnitTest")]
+        public void MakePermanentTest()
+        {
+
+            List<Point> points = new List<Point>();
+            points.AddRange(DebugData.debugPoints4);
+            InternalLine uut = new InternalLine(points);
+            Assert.AreEqual(false, uut.isPoint);
+            uut.MakePermanent(5);
+            Assert.AreEqual(true, uut.isPoint);
+            Assert.AreEqual(5, uut.GetID());
+            Assert.AreEqual(0, uut.GetLength());
+        }
+
+        [DataTestMethod]
+        [TestCategory("UnitTest")]
+        [DataRow(new int[] { 1 ,1, 3, 3 }, new int[] { 1, 1, 2, 2, 3, 3 }, false, 2.828427125)]
+        [DataRow(new int[] { 1, 1, 3, 3 }, new int[] { 1, 1, 2, 2, 3, 3 }, true, 2.828427125)]
+        [DataRow(new int[] { 1, 1, 1, 4 ,3, 4 }, new int[] { 1, 1, 1, 2, 1, 3, 1, 4, 2, 4, 3, 4 }, false, 5)]
+        [DataRow(new int[] { 1, 1, 1, 4, 3, 4 }, new int[] { 1, 1, 1, 2, 1, 3, 1, 4, 2, 4, 3, 4 }, true, 5)]
+        public void PermanentLineTest(int[] inPoints, int[] outPoints, bool isTemp, double len)
+        {
+            List<Point> inLine = new List<Point>(); List<Point> outLine = new List<Point>();
+            for (int i = 0; i < inPoints.Length;i += 2) inLine.Add(new Point(inPoints[i], inPoints[i + 1]));
+            for (int i = 0; i < outPoints.Length; i += 2) outLine.Add(new Point(outPoints[i], outPoints[i + 1]));
+            InternalLine uut;
+            if (isTemp)
+            {
+                uut = new InternalLine(inLine);
+                var zip = inLine.Zip(uut.GetPoints(), (a,b) => new Tuple<Point, Point>(a, b));
+                foreach (Tuple < Point, Point> tup in zip)
+                {
+                    Assert.AreEqual(tup.Item1, tup.Item2);
+                }
+            }
+            else
+            {
+                uut = new InternalLine(inLine,0);
+                var zip = outLine.Zip(uut.GetPoints(), (a, b) => new Tuple<Point, Point>(a, b));
+                foreach (Tuple<Point, Point> tup in zip)
+                {
+                    Assert.AreEqual(tup.Item1, tup.Item2);
+                }
+            }
+            Assert.AreEqual(len, uut.GetLength(), 0.000001);
+        }
+    }
+}

+ 60 - 10
SketchAssistant/SketchAssistant.Tests/SketchAssistant.Tests.csproj → SketchAssistant/WhiteTests/WhiteTests.csproj

@@ -1,15 +1,16 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="..\packages\ReportGenerator.4.0.15\build\netstandard2.0\ReportGenerator.props" Condition="Exists('..\packages\ReportGenerator.4.0.15\build\netstandard2.0\ReportGenerator.props')" />
   <Import Project="..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.props')" />
   <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProjectGuid>{7DCDC31A-8291-4B05-93D6-DCC5DE27A4A0}</ProjectGuid>
+    <ProjectGuid>{EB09C624-91F2-465F-825B-559BF7A7D5CB}</ProjectGuid>
     <OutputType>Library</OutputType>
     <AppDesignerFolder>Properties</AppDesignerFolder>
-    <RootNamespace>SketchAssistant.Tests</RootNamespace>
-    <AssemblyName>SketchAssistant.Tests</AssemblyName>
+    <RootNamespace>WhiteTests</RootNamespace>
+    <AssemblyName>WhiteTests</AssemblyName>
     <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
     <FileAlignment>512</FileAlignment>
     <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
@@ -38,23 +39,36 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
+  <PropertyGroup>
+    <StartupObject />
+  </PropertyGroup>
   <ItemGroup>
+    <Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
+      <HintPath>..\packages\Castle.Core.4.3.1\lib\net45\Castle.Core.dll</HintPath>
+    </Reference>
     <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
       <HintPath>..\packages\MSTest.TestFramework.1.4.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
     </Reference>
     <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
       <HintPath>..\packages\MSTest.TestFramework.1.4.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
     </Reference>
+    <Reference Include="PresentationFramework" />
     <Reference Include="System" />
+    <Reference Include="System.Configuration" />
     <Reference Include="System.Core" />
     <Reference Include="System.Drawing" />
-    <Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
-      <HintPath>..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll</HintPath>
-    </Reference>
+    <Reference Include="System.Windows" />
     <Reference Include="System.Windows.Forms" />
+    <Reference Include="TestStack.White, Version=0.13.0.0, Culture=neutral, PublicKeyToken=2672efbf3e161801, processorArchitecture=MSIL">
+      <HintPath>..\packages\TestStack.White.0.13.3\lib\net40\TestStack.White.dll</HintPath>
+    </Reference>
+    <Reference Include="WindowsBase" />
+    <Reference Include="WindowsInput, Version=1.0.4.0, Culture=neutral, PublicKeyToken=9b287f7dc5073cad, processorArchitecture=MSIL">
+      <HintPath>..\packages\InputSimulator.1.0.4.0\lib\net20\WindowsInput.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="UnitTest1.cs" />
+    <Compile Include="UITest.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>
   <ItemGroup>
@@ -62,11 +76,42 @@
     <None Include="packages.config" />
   </ItemGroup>
   <ItemGroup>
-    <ProjectReference Include="..\SketchAssistant\SketchAssistant.csproj">
-      <Project>{0336f628-a2f7-4170-8b2e-9277c23118d4}</Project>
-      <Name>SketchAssistant</Name>
+    <ProjectReference Include="..\SketchAssistantWPF\SketchAssistantWPF.csproj">
+      <Project>{ee53ae79-2aa0-4f43-9638-1789b189d5c3}</Project>
+      <Name>SketchAssistantWPF</Name>
     </ProjectReference>
   </ItemGroup>
+  <ItemGroup />
+  <ItemGroup>
+    <Content Include="test_input_files\blacklisted\all_no_size_def_in_header.svg">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+    <Content Include="test_input_files\blacklisted\all_unclosed.svg">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+    <Content Include="test_input_files\whitelisted\all.svg">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+    <Content Include="test_input_files\whitelisted\circle_ellipse.svg">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+    <Content Include="test_input_files\whitelisted\line.svg">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+    <Content Include="test_input_files\whitelisted\path.svg">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+    <Content Include="test_input_files\whitelisted\polygon.svg">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+    <Content Include="test_input_files\whitelisted\polyline.svg">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+    <Content Include="test_input_files\whitelisted\rect.svg">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+    <None Include="test.runsettings" />
+  </ItemGroup>
   <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
@@ -75,6 +120,11 @@
     </PropertyGroup>
     <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.props'))" />
     <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.targets'))" />
+    <Error Condition="!Exists('..\packages\ReportGenerator.4.0.15\build\netstandard2.0\ReportGenerator.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\ReportGenerator.4.0.15\build\netstandard2.0\ReportGenerator.props'))" />
   </Target>
   <Import Project="..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.targets')" />
+  <PropertyGroup>
+    <PostBuildEvent>
+    </PostBuildEvent>
+  </PropertyGroup>
 </Project>

+ 2 - 2
SketchAssistant/SketchAssistant.Tests/app.config → SketchAssistant/WhiteTests/app.config

@@ -3,8 +3,8 @@
   <runtime>
     <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
       <dependentAssembly>
-        <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
-        <bindingRedirect oldVersion="0.0.0.0-4.0.4.1" newVersion="4.0.4.1" />
+        <assemblyIdentity name="Castle.Core" publicKeyToken="407dd0808d44fbdc" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
       </dependentAssembly>
     </assemblyBinding>
   </runtime>

+ 11 - 0
SketchAssistant/WhiteTests/packages.config

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="Castle.Core" version="4.3.1" targetFramework="net461" />
+  <package id="InputSimulator" version="1.0.4.0" targetFramework="net461" />
+  <package id="Microsoft.TestPlatform" version="16.0.1" targetFramework="net461" />
+  <package id="MSTest.TestAdapter" version="1.4.0" targetFramework="net461" />
+  <package id="MSTest.TestFramework" version="1.4.0" targetFramework="net461" />
+  <package id="OpenCover" version="4.7.922" targetFramework="net461" />
+  <package id="ReportGenerator" version="4.0.15" targetFramework="net461" />
+  <package id="TestStack.White" version="0.13.3" targetFramework="net461" />
+</packages>

+ 10 - 0
SketchAssistant/WhiteTests/test.runsettings

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<RunSettings>
+  <!-- MSTest adapter -->
+  <MSTest>
+    <MapInconclusiveToFailed>True</MapInconclusiveToFailed>
+    <DeleteDeploymentDirectoryAfterTestRunIsComplete>False</DeleteDeploymentDirectoryAfterTestRunIsComplete>
+    <DeploymentEnabled>False</DeploymentEnabled>
+  </MSTest>
+
+</RunSettings>

+ 30 - 0
SketchAssistant/WhiteTests/test_input_files/blacklisted/all_no_size_def_in_header.svg

@@ -0,0 +1,30 @@
+fsfwefafaefcSC
+F
+S
+FSFewFAHBfAg
+<DSFVSF>
+<GSG FS02MAFfm="Fmseikf" />
+source: https://www.w3schools.com/
+<svg>
+  <polygon points="220,10 300,210 170,250 123,234" style="fill:lime;stroke:purple;stroke-width:1" />
+  <polyline points="20,20 40,25 60,40 80,120 120,140 200,180"
+  style="fill:none;stroke:black;stroke-width:3" />
+    <line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" />
+  <rect x="50" y="20" width="150" height="150"
+  style="fill:blue;stroke:pink;stroke-width:5;fill-opacity:0.1;stroke-opacity:0.9" />
+  <rect x="352.324" y="311.765" fill="#5486F7" width="4" height="128"/>
+  <rect x="478.871" y="266.351" fill="#FFFFFF" width="12" height="12"/>
+  <ellipse cx="250" cy="250" rx="100" ry="50"
+  style="fill:yellow;stroke:purple;stroke-width:2" />
+  <ellipse cx="240" cy="100" rx="220" ry="30" style="fill:purple" />
+  <ellipse cx="220" cy="70" rx="190" ry="20" style="fill:lime" />
+  <ellipse cx="210" cy="45" rx="170" ry="15" style="fill:yellow" />
+  <path id="lineAB" d="M 100 350 l 150 -300" stroke="red"
+  stroke-width="3" fill="none" />
+  <path id="lineBC" d="M 250 50 l 150 300" stroke="red"
+  stroke-width="3" fill="none" />
+  <path d="M 175 200 l 150 0" stroke="green" stroke-width="3"
+  fill="none" />
+  <path d="M 100 350 q 150 -300 300 0" stroke="blue"
+  stroke-width="5" fill="none" />
+</svg>

+ 29 - 0
SketchAssistant/WhiteTests/test_input_files/blacklisted/all_unclosed.svg

@@ -0,0 +1,29 @@
+fsfwefafaefcSC
+F
+S
+FSFewFAHBfAg
+<DSFVSF>
+<GSG FS02MAFfm="Fmseikf" />
+source: https://www.w3schools.com/
+<svg height="500" width="500">
+  <polygon points="220,10 300,210 170,250 123,234" style="fill:lime;stroke:purple;stroke-width:1" />
+  <polyline points="20,20 40,25 60,40 80,120 120,140 200,180"
+  style="fill:none;stroke:black;stroke-width:3" />
+    <line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" />
+  <rect x="50" y="20" width="150" height="150"
+  style="fill:blue;stroke:pink;stroke-width:5;fill-opacity:0.1;stroke-opacity:0.9" />
+  <rect x="352.324" y="311.765" fill="#5486F7" width="4" height="128"/>
+  <rect x="478.871" y="266.351" fill="#FFFFFF" width="12" height="12"/>
+  <ellipse cx="250" cy="250" rx="100" ry="50"
+  style="fill:yellow;stroke:purple;stroke-width:2" />
+  <ellipse cx="240" cy="100" rx="220" ry="30" style="fill:purple" />
+  <ellipse cx="220" cy="70" rx="190" ry="20" style="fill:lime" />
+  <ellipse cx="210" cy="45" rx="170" ry="15" style="fill:yellow" />
+  <path id="lineAB" d="M 100 350 l 150 -300" stroke="red"
+  stroke-width="3" fill="none" />
+  <path id="lineBC" d="M 250 50 l 150 300" stroke="red"
+  stroke-width="3" fill="none" />
+  <path d="M 175 200 l 150 0" stroke="green" stroke-width="3"
+  fill="none" />
+  <path d="M 100 350 q 150 -300 300 0" stroke="blue"
+  stroke-width="5" fill="none" />

+ 30 - 0
SketchAssistant/WhiteTests/test_input_files/whitelisted/all.svg

@@ -0,0 +1,30 @@
+fsfwefafaefcSC
+F
+S
+FSFewFAHBfAg
+<DSFVSF>
+<GSG FS02MAFfm="Fmseikf" />
+source: https://www.w3schools.com/
+<svg height="500" width="500">
+  <polygon points="220,10 300,210 170,250 123,234" style="fill:lime;stroke:purple;stroke-width:1" />
+  <polyline points="20,20 40,25 60,40 80,120 120,140 200,180"
+  style="fill:none;stroke:black;stroke-width:3" />
+    <line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" />
+  <rect x="50" y="20" width="150" height="150"
+  style="fill:blue;stroke:pink;stroke-width:5;fill-opacity:0.1;stroke-opacity:0.9" />
+  <rect x="352.324" y="311.765" fill="#5486F7" width="4" height="128"/>
+  <rect x="478.871" y="266.351" fill="#FFFFFF" width="12" height="12"/>
+  <ellipse cx="250" cy="250" rx="100" ry="50"
+  style="fill:yellow;stroke:purple;stroke-width:2" />
+  <ellipse cx="240" cy="100" rx="220" ry="30" style="fill:purple" />
+  <ellipse cx="220" cy="70" rx="190" ry="20" style="fill:lime" />
+  <ellipse cx="210" cy="45" rx="170" ry="15" style="fill:yellow" />
+  <path id="lineAB" d="M 100 350 l 150 -300" stroke="red"
+  stroke-width="3" fill="none" />
+  <path id="lineBC" d="M 250 50 l 150 300" stroke="red"
+  stroke-width="3" fill="none" />
+  <path d="M 175 200 l 150 0" stroke="green" stroke-width="3"
+  fill="none" />
+  <path d="M 100 350 q 150 -300 300 0" stroke="blue"
+  stroke-width="5" fill="none" />
+</svg>

+ 14 - 0
SketchAssistant/WhiteTests/test_input_files/whitelisted/circle_ellipse.svg

@@ -0,0 +1,14 @@
+fsfwefafaefcSC
+F
+S
+FSFewFAHBfAg
+<DSFVSF>
+<GSG FS02MAFfm="Fmseikf" />
+source: https://www.w3schools.com/
+<svg height="5000" width="5000">
+  <ellipse cx="2500" cy="2500" rx="1000" ry="500"
+  style="fill:yellow;stroke:purple;stroke-width:2" />
+  <ellipse cx="2400" cy="1000" rx="2200" ry="300" style="fill:purple" />
+  <ellipse cx="2200" cy="700" rx="1900" ry="200" style="fill:lime" />
+  <ellipse cx="2100" cy="450" rx="1700" ry="150" style="fill:yellow" />
+</svg>

+ 10 - 0
SketchAssistant/WhiteTests/test_input_files/whitelisted/line.svg

@@ -0,0 +1,10 @@
+fsfwefafaefcSC
+F
+S
+FSFewFAHBfAg
+<DSFVSF>
+<GSG FS02MAFfm="Fmseikf" />
+source: https://www.w3schools.com/
+<svg height="500" width="500">
+  <line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" />
+</svg>

+ 21 - 0
SketchAssistant/WhiteTests/test_input_files/whitelisted/path.svg

@@ -0,0 +1,21 @@
+fsfwefafaefcSC
+F
+S
+FSFewFAHBfAg
+<DSFVSF>
+<GSG FS02MAFfm="Fmseikf" />
+source: https://www.w3schools.com/
+<svg height="600" width="1200">
+  <path id="lineAB" d="M 100 350 l 150 -300" stroke="red"
+  stroke-width="3" fill="none" />
+  <path id="lineBC" d="M 250 50 l 150 300" stroke="red"
+  stroke-width="3" fill="none" />
+  <path d="M 100 350 q 150 -300 300 0 z M 175 200 l 150 0" stroke="blue"
+  stroke-width="5" fill="none" />
+  <path d="M 100 100 a 60 60 0 0 0 100 0 Z"
+      fill="yellow" stroke="blue" stroke-width="5" />
+  <path d="M 100 200 a 150 120 45 1 0 100 100"
+      fill="yellow" stroke="blue" stroke-width="5" />
+  <path d="M200,300 Q400,50 600,300 T1000,300"
+        fill="none" stroke="red" stroke-width="5"  />
+</svg>

+ 10 - 0
SketchAssistant/WhiteTests/test_input_files/whitelisted/polygon.svg

@@ -0,0 +1,10 @@
+fsfwefafaefcSC
+F
+S
+FSFewFAHBfAg
+<DSFVSF>
+<GSG FS02MAFfm="Fmseikf" />
+source: https://www.w3schools.com/
+<svg height="500" width="500">
+  <polygon points="220,10 300,210 170,250 123,234" style="fill:lime;stroke:purple;stroke-width:1" />
+</svg>

+ 11 - 0
SketchAssistant/WhiteTests/test_input_files/whitelisted/polyline.svg

@@ -0,0 +1,11 @@
+fsfwefafaefcSC
+F
+S
+FSFewFAHBfAg
+<DSFVSF>
+<GSG FS02MAFfm="Fmseikf" />
+source: https://www.w3schools.com/
+<svg height="500" width="500">
+  <polyline points="20,20 40,25 60,40 80,120 120,140 200,180"
+  style="fill:none;stroke:black;stroke-width:3" />
+</svg>

+ 13 - 0
SketchAssistant/WhiteTests/test_input_files/whitelisted/rect.svg

@@ -0,0 +1,13 @@
+fsfwefafaefcSC
+F
+S
+FSFewFAHBfAg
+<DSFVSF>
+<GSG FS02MAFfm="Fmseikf" />
+source: https://www.w3schools.com/
+<svg height="500" width="500">
+  <rect x="50" y="20" width="150" height="150"
+  style="fill:blue;stroke:pink;stroke-width:5;fill-opacity:0.1;stroke-opacity:0.9" />
+  <rect x="352.324" y="311.765" fill="#5486F7" width="4" height="128"/>
+  <rect x="478.871" y="266.351" fill="#FFFFFF" width="12" height="12"/>
+</svg>

+ 126 - 0
screenres.ps1

@@ -0,0 +1,126 @@
+# http://blogs.technet.com/b/heyscriptingguy/archive/2010/07/07/hey-scripting-guy-how-can-i-change-my-desktop-monitor-resolution-via-windows-powershell.aspx
+
+Function Set-ScreenResolution { 
+param ( 
+[Parameter(Mandatory=$true, 
+           Position = 0)] 
+[int] 
+$Width, 
+[Parameter(Mandatory=$true, 
+           Position = 1)] 
+[int] 
+$Height 
+) 
+$pinvokeCode = @" 
+using System; 
+using System.Runtime.InteropServices; 
+namespace Resolution 
+{ 
+    [StructLayout(LayoutKind.Sequential)] 
+    public struct DEVMODE1 
+    { 
+        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] 
+        public string dmDeviceName; 
+        public short dmSpecVersion; 
+        public short dmDriverVersion; 
+        public short dmSize; 
+        public short dmDriverExtra; 
+        public int dmFields; 
+        public short dmOrientation; 
+        public short dmPaperSize; 
+        public short dmPaperLength; 
+        public short dmPaperWidth; 
+        public short dmScale; 
+        public short dmCopies; 
+        public short dmDefaultSource; 
+        public short dmPrintQuality; 
+        public short dmColor; 
+        public short dmDuplex; 
+        public short dmYResolution; 
+        public short dmTTOption; 
+        public short dmCollate; 
+        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] 
+        public string dmFormName; 
+        public short dmLogPixels; 
+        public short dmBitsPerPel; 
+        public int dmPelsWidth; 
+        public int dmPelsHeight; 
+        public int dmDisplayFlags; 
+        public int dmDisplayFrequency; 
+        public int dmICMMethod; 
+        public int dmICMIntent; 
+        public int dmMediaType; 
+        public int dmDitherType; 
+        public int dmReserved1; 
+        public int dmReserved2; 
+        public int dmPanningWidth; 
+        public int dmPanningHeight; 
+    }; 
+    class User_32 
+    { 
+        [DllImport("user32.dll")] 
+        public static extern int EnumDisplaySettings(string deviceName, int modeNum, ref DEVMODE1 devMode); 
+        [DllImport("user32.dll")] 
+        public static extern int ChangeDisplaySettings(ref DEVMODE1 devMode, int flags); 
+        public const int ENUM_CURRENT_SETTINGS = -1; 
+        public const int CDS_UPDATEREGISTRY = 0x01; 
+        public const int CDS_TEST = 0x02; 
+        public const int DISP_CHANGE_SUCCESSFUL = 0; 
+        public const int DISP_CHANGE_RESTART = 1; 
+        public const int DISP_CHANGE_FAILED = -1; 
+    } 
+    public class PrmaryScreenResolution 
+    { 
+        static public string ChangeResolution(int width, int height) 
+        { 
+            DEVMODE1 dm = GetDevMode1(); 
+            if (0 != User_32.EnumDisplaySettings(null, User_32.ENUM_CURRENT_SETTINGS, ref dm)) 
+            { 
+                dm.dmPelsWidth = width; 
+                dm.dmPelsHeight = height; 
+                int iRet = User_32.ChangeDisplaySettings(ref dm, User_32.CDS_TEST); 
+                if (iRet == User_32.DISP_CHANGE_FAILED) 
+                { 
+                    return "Unable To Process Your Request. Sorry For This Inconvenience."; 
+                } 
+                else 
+                { 
+                    iRet = User_32.ChangeDisplaySettings(ref dm, User_32.CDS_UPDATEREGISTRY); 
+                    switch (iRet) 
+                    { 
+                        case User_32.DISP_CHANGE_SUCCESSFUL: 
+                            { 
+                                return "Success"; 
+                            } 
+                        case User_32.DISP_CHANGE_RESTART: 
+                            { 
+                                return "You Need To Reboot For The Change To Happen.\n If You Feel Any Problem After Rebooting Your Machine\nThen Try To Change Resolution In Safe Mode."; 
+                            } 
+                        default: 
+                            { 
+                                return "Failed To Change The Resolution"; 
+                            } 
+                    } 
+                } 
+            } 
+            else 
+            { 
+                return "Failed To Change The Resolution."; 
+            } 
+        } 
+        private static DEVMODE1 GetDevMode1() 
+        { 
+            DEVMODE1 dm = new DEVMODE1(); 
+            dm.dmDeviceName = new String(new char[32]); 
+            dm.dmFormName = new String(new char[32]); 
+            dm.dmSize = (short)Marshal.SizeOf(dm); 
+            return dm; 
+        } 
+    } 
+} 
+"@ 
+Add-Type $pinvokeCode -ErrorAction SilentlyContinue 
+[Resolution.PrmaryScreenResolution]::ChangeResolution($width,$height) 
+}
+
+Set-ScreenResolution 1920 1080

Неке датотеке нису приказане због велике количине промена