52 Commits 521251cd30 ... 0a045c3164

Author SHA1 Message Date
  Martin Edlund (Laptop) 0a045c3164 Cleaned up code for codereview 5 years ago
  Martin Edlund 0ad6d3bf8f Fixed build error from rename 5 years ago
  Martin Edlund d64910ea7f Fixed variable names 5 years ago
  Martin Edlund cfbbf8e1d1 Cleaned up before pull request 5 years ago
  Martin Edlund 5f75b2cf0f Maybe fixed error 5 years ago
  Martin Edlund afb24e7664 Minor fixes 5 years ago
  Martin Edlund (Laptop) fbd014f5af Added function to calculate when to vibrate pen 5 years ago
  Martin Edlund (Laptop) e94b04a25f Another minor change 5 years ago
  Martin Edlund (Laptop) 2d8166c5ac Added check to prevent drawing when moving back through drawing plane 5 years ago
  Martin Edlund (Laptop) c1a2ff4d09 Fixed error when changing drawing mode while drawing mid air 5 years ago
  videowall-pc-user 08720b51f8 Calibrated Color and Opacity 5 years ago
  Martin Edlund (Laptop) 7220499631 Added color options 5 years ago
  videowall-pc-user 8a5b3303bc Fixed errors 5 years ago
  Martin Edlund (Laptop) af1e107195 maybe fixed a bug 5 years ago
  videowall-pc-user 50984af921 calibrated parameters for converting optitrack to pixel 5 years ago
  Martin Edlund (Laptop) 190ce1d36a Minor change 5 years ago
  Martin Edlund (Laptop) 1216a01a42 Another Minor change 5 years ago
  Martin Edlund (Laptop) 358be1b0bb minor change 5 years ago
  Martin Edlund 2cc1f90ce2 Rewrote internal optitrack drawing & deleting logic 5 years ago
  Martin Edlund bf6a6d9a72 Merge branch 'develop' into userstory20 5 years ago
  Martin Edlund 886404d7b9 Fixed missing functionality after merge 5 years ago
  Martin Edlund caf72c4d19 Merge branch 'develop' into userstory20 5 years ago
  Dennymany f8a9b70c08 removed old ttp, replaced new ttp 5 years ago
  m-edlund f4674b5efd Userstory13 (#16) 5 years ago
  m-edlund aee756b8f8 Hotfix (#15) 5 years ago
  m-edlund e93039223e Userstory8 (#14) 5 years ago
  m-edlund 68f620d518 Userstory21 (#13) 5 years ago
  Rumei a229ef486e slight changes to program to suite codecoverage runs 5 years ago
  Vincenz Mechler b3e1f256e7 hotfix, added exception handling to isad import method call and added missing method comment to svg import ui method 5 years ago
  Vincenz Mechler 8e8ce61fb2 removed unnecessary files 5 years ago
  Vincenz Mechler 23ac7af994 excluded svg parsing tests from AppVeyor execution as they depend on filesystem access and the [DeploymentItem] attribute 5 years ago
  Vincenz Mechler 9e858f3e38 moved FileImporter test class to UITest.cs, adapted relative file paths 5 years ago
  Vincenz Mechler fb30fd704e merged develop into userstory2 in preperation for backward merging 5 years ago
  Vincenz Mechler e9ad799202 added comments to FileImporter tests 5 years ago
  chooga e6f3cbdba9 Update userstory6.md 5 years ago
  Martin Edlund 9988b5b6e0 Optimized InternalLine 5 years ago
  Vincenz Mechler c2b20ea890 finished basic tests for svg importer and added a .testsettings file 5 years ago
  Martin Edlund 46a6fe9e8e Hotfix + Cleaned up Project Folder 5 years ago
  m-edlund 28eaf0e7d2 Userstory16 (#11) 5 years ago
  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 years ago
  Martin Edlund 2ff4f32409 Merge branch 'develop' into userstory2 5 years ago
  Martin Edlund f754e5a215 Removed empty constructor 5 years ago
  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 years ago
  Vincenz Mechler 2fe93d8927 cleanup and added error for unterminated svg files 5 years ago
  Vincenz Mechler ae6c32073e fixed elliptical arc coordinate mismatch bug 5 years ago
  Vincenz Mechler 1302db3636 fixed elliptical arc rotation bug 5 years ago
  Vincenz Mechler 7ade1eb4d2 fixed a few bugs 5 years ago
  Vincenz Mechler 1f90174628 implemented path data normalization, fixed a bug when parsing closepath path elements 5 years ago
  Vincenz Mechler 10d9bc9959 implemented svg path parsing (except for ellipse path elements (A and a)) 5 years ago
  Vincenz Mechler 541bd6657c added folder for test input files and created simple svgs 5 years ago
  Vincenz Mechler 1f0e044436 implemented ellipse sampling and deactivated path parsing for now 5 years ago
  Vincenz Mechler e50cdc5fa8 started implementing svg parsing: implemented rect, line, polyline, polygon parsing, created circle, ellipse stubs, begun implementation of path parsing 5 years ago

+ 5 - 6
.appveyor.yml

@@ -19,9 +19,8 @@ artifacts:
 
 before_test:
     - ps: "SketchAssistant/GenerateCoverageReport.bat"
-
-test:
-    #don't run tests depending on [DeploymentItem] and filesystem access
-    categories:
-        except:
-            - FileIO
+#test:
+#don't run tests depending on [DeploymentItem] and filesystem access
+#categories:
+#    except:
+#        - FileIO

+ 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/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|

+ 3 - 0
SketchAssistant/.gitignore

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

+ 15 - 3
SketchAssistant/GenerateCoverageReport.bat

@@ -1,13 +1,25 @@
 if not exist "%~dp0GeneratedReports" mkdir "%~dp0GeneratedReports"
 
-"%~dp0\packages\OpenCover.4.7.922\tools\OpenCover.Console.exe" ^
+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:"%~dp0\packages\Microsoft.TestPlatform.16.0.0\tools\net451\Common7\IDE\Extensions\TestPlatform\vstest.console.exe" ^
+-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"
 
-"%~dp0\packages\ReportGenerator.4.0.14\tools\net47\ReportGenerator.exe" ^
+"%repGen%\tools\net47\ReportGenerator.exe" ^
 -reports:"%~dp0\GeneratedReports\opencovertests.xml" ^
 -targetdir:"%~dp0\GeneratedReports\ReportGeneratorOutput"

+ 2 - 1
SketchAssistant/SketchAssistantWPF/App.xaml

@@ -2,7 +2,8 @@
              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">
+             StartupUri="MainWindow.xaml"
+             ShutdownMode="OnMainWindowClose">
     <Application.Resources>
          
     </Application.Resources>

+ 0 - 70
SketchAssistant/SketchAssistantWPF/Armband.cs

@@ -1,70 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-using System.Threading;
-
-namespace SketchAssistantWPF
-{
-    internal class Armband
-    {
-        //[StructLayout(LayoutKind.Sequential)]
-        //public class BodyActuator
-        //{
-        //    enum BodyActuator_Type
-        //    {
-        //        BODYACTUATOR_TYPE_NONE,
-        //        BODYACTUATOR_TYPE_EAI,
-        //        BODYACTUATOR_TYPE_PIEZO,
-        //        BODYACTUATOR_TYPE_ERM,
-        //        BODYACTUATOR_TYPE_EMS
-        //    };
-        //    bool valid;
-        //    ushort actuatorCount;
-        //    BodyActuator_Type type;
-        //    ArduinoHub arduinoHub;
-        //    EAIHub eaiHub;
-        //}
-
-
-        //[StructLayout(LayoutKind.Sequential)]
-        //public class EAIHub
-        //{
-        //    bool valid;
-        //    pthread_mutex_t mutex; //TODO fix this
-        //    pthread_t thread;
-        //    Actuator* actuators; //TODO
-        //    uint tactorType;
-        //    uint modulation;
-        //    int deviceID;
-        //}
-
-        //[StructLayout(LayoutKind.Sequential)]
-        //public class ArduinoHub
-        //{
-        //    enum ArduinoHub_Type
-        //    {
-        //        ARDUINOHUB_TYPE_PIEZO = 'P',
-        //        ARDUINOHUB_TYPE_ERM = 'E',
-        //        ARDUINOHUB_TYPE_EMS = 'M'
-        //    }
-        //    bool valid;
-        //    pthread_mutex_t mutex; //TODO fix this
-        //    pthread_t thread;
-        //    ArduinoHub_Type arduinoType;
-        //    Serial* serial;
-        //    Actuator* actuators;
-        //}
-
-        //[DllImport("BodyActuator.dll", EntryPoint = "BodyActuator_actuate")]
-        //static extern void pushForward(ref BodyActuator self, byte tactor, double intensity, ulong duration);
-
-        internal void pushForward()
-        {
-            Console.WriteLine("FORWARD_PUSH");
-        }
-
-        internal void pushBackward()
-        {
-            Console.WriteLine("BACKWARD_PUSH");
-        }
-    }
-}

File diff suppressed because it is too large
+ 4 - 0
SketchAssistant/SketchAssistantWPF/DebugData.cs


+ 1 - 1
SketchAssistant/SketchAssistantWPF/FileImporter.cs

@@ -1684,4 +1684,4 @@ namespace SketchAssistantWPF
             return ellipse;
         }
     }
-}
+}

+ 144 - 0
SketchAssistant/SketchAssistantWPF/GeometryCalculator.cs

@@ -12,6 +12,150 @@ namespace SketchAssistantWPF
     /// </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>

+ 35 - 7
SketchAssistant/SketchAssistantWPF/InternalLine.cs

@@ -34,6 +34,10 @@ namespace SketchAssistantWPF
         /// 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.
@@ -62,14 +66,18 @@ namespace SketchAssistantWPF
             isTemporary = false;
         }
 
-        public Point GetStartPoint()
-        {
-            return linePoints.First();
-        }
-
-        public Point GetEndPoint()
+        /// <summary>
+        /// A function to make temporary lines non-temporary.
+        /// </summary>
+        /// <param name="id">The id of the line.</param>
+        public void MakePermanent(int id)
         {
-            return linePoints.Last();
+            if (isTemporary)
+            {
+                identifier = id;
+                CleanPoints();
+                isTemporary = false;
+            }
         }
 
         public List<Point> GetPoints()
@@ -87,6 +95,26 @@ namespace SketchAssistantWPF
             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>

+ 200 - 191
SketchAssistant/SketchAssistantWPF/MVP_Model.cs

@@ -23,17 +23,25 @@ namespace SketchAssistantWPF
         /// History of Actions
         /// </summary>
         ActionHistory historyOfActions;
-
+        /// <summary>
+        /// The connector class used to get frames from the Optitrack system.
+        /// </summary>
         OptiTrackConnector connector;
 
+
+
         /***********************/
         /*** CLASS VARIABLES ***/
         /***********************/
 
+        /// <summary>
+        /// this is a variable used for detecting whether the tracker is in the warning zone (0 +- variable), no drawing zone (0 +- 2 * variable) or normal drawing zone
+        /// </summary>
+        readonly double WARNING_ZONE_BOUNDARY = 0.10; //10cm
         /// <summary>
         /// If the program is in drawing mode.
         /// </summary>
-        bool inDrawingMode;
+        public bool inDrawingMode { get; private set; }
         /// <summary>
         /// if the program is using OptiTrack
         /// </summary>
@@ -43,10 +51,6 @@ namespace SketchAssistantWPF
         /// </summary>
         int deletionRadius = 5;
         /// <summary>
-        /// Size of areas marking endpoints of lines in the redraw mode.
-        /// </summary>
-        int markerRadius = 10;
-        /// <summary>
         /// The Position of the Cursor in the right picture box
         /// </summary>
         Point currentCursorPosition;
@@ -59,6 +63,18 @@ namespace SketchAssistantWPF
         /// </summary>
         Queue<Point> cursorPositions = new Queue<Point>();
         /// <summary>
+        /// The Position of the Cursor of opti track
+        /// </summary>
+        Point currentOptiCursorPosition;
+        /// <summary>
+        /// The Previous Cursor Position of opti track
+        /// </summary>
+        Point previousOptiCursorPosition;
+        /// <summary>
+        /// Queue for the cursorPositions of opti track
+        /// </summary>
+        Queue<Point> optiCursorPositions = new Queue<Point>();
+        /// <summary>
         /// Lookup Matrix for checking postions of lines in the image
         /// </summary>
         bool[,] isFilledMatrix;
@@ -67,10 +83,6 @@ namespace SketchAssistantWPF
         /// </summary>
         HashSet<int>[,] linesMatrix;
         /// <summary>
-        /// List of items which will be overlayed over the right canvas.
-        /// </summary>
-        List<Tuple<bool, HashSet<Point>>> overlayItems;
-        /// <summary>
         /// Width of the LeftImageBox.
         /// </summary>
         public int leftImageBoxWidth;
@@ -86,9 +98,9 @@ namespace SketchAssistantWPF
         /// Height of the RightImageBox.
         /// </summary>
         public int rightImageBoxHeight;
-
-        public ImageDimension leftImageSize { get; private set; }
-
+        /// <summary>
+        /// The size of the right canvas.
+        /// </summary>
         public ImageDimension rightImageSize { get; private set; }
         /// <summary>
         /// Indicates whether or not the canvas on the right side is active.
@@ -103,15 +115,15 @@ namespace SketchAssistantWPF
         /// </summary>
         public bool optitrackAvailable { get; private set; }
         /// <summary>
-        /// x koordinate in real world. one unit is one meter. If standing in front of video wall facing it, moving left results in incrementation of x.
+        /// x coordinate in real world. one unit is one meter. If standing in front of video wall facing it, moving left results in incrementation of x.
         /// </summary>
         public float optiTrackX;
         /// <summary>
-        /// y koordinate in real world. one unit is one meter. If standing in front of video wall, moving up results in incrementation of y.
+        /// y coordinate in real world. one unit is one meter. If standing in front of video wall, moving up results in incrementation of y.
         /// </summary>
         public float optiTrackY;
         /// <summary>
-        /// z koordinate in real world. one unit is one meter. If standing in front of video wall, moving back results in incrementation of y.
+        /// z coordinate in real world. one unit is one meter. If standing in front of video wall, moving back results in incrementation of y.
         /// </summary>
         public float optiTrackZ;
         /// <summary>
@@ -119,75 +131,91 @@ namespace SketchAssistantWPF
         /// </summary>
         private bool optiTrackInsideDrawingZone = false;
         /// <summary>
-        /// this is a variable used for detecting whether the tracker is in the warning zone (0 +- variable), no drawing zone (0 +- 2 * variable) or normal drawing zone
+        /// object of class wristband used for controlling the vibrotactile wristband
         /// </summary>
-        private double WARNING_ZONE_BOUNDARY = 0.10; //10cm
+        private Wristband wristband;
         /// <summary>
-        /// object of class Armband used for controlling the vibrotactil armband
+        /// Is set to true when the trackable has passed to the backside of the drawing surface, 
+        /// invalidating all inputs on its way back.
         /// </summary>
-        private Armband armband;
+        bool OptiMovingBack = false;
         /// <summary>
-        /// deactivates all Console.WriteLine() when set to false
+        /// The Layer in which the optitrack system was. 0 is drawing layer, -1 is in front, 1 is behind
         /// </summary>
-        bool testing = false;//TODO: remove after finishing userstory
-
-
-        Image rightImageWithoutOverlay;
+        int OptiLayer = 0;
+        /// <summary>
+        /// The path traveled since the last tick
+        /// </summary>
+        double PathTraveled = 0;
         /// <summary>
         /// Whether or not the mouse is pressed.
         /// </summary>
         private bool mouseDown;
-
+        /// <summary>
+        /// A List of lines in the left canvas.
+        /// </summary>
         List<InternalLine> leftLineList;
-
+        /// <summary>
+        /// A list of lines in the right canvas along with a boolean indicating if they should be drawn.
+        /// </summary>
         List<Tuple<bool, InternalLine>> rightLineList;
-
+        /// <summary>
+        /// The line currently being drawin with optitrack.
+        /// </summary>
         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);
             connector = new OptiTrackConnector();
-            armband = new Armband();
+            wristband = new Wristband();
 
+            //Set up Optitrack
             optitrackAvailable = false;
             if (File.Exists(@"C:\Users\videowall-pc-user\Documents\BP-SketchAssistant\SketchAssistant\optitrack_setup.ttp"))
             {
                 if (connector.Init(@"C:\Users\videowall-pc-user\Documents\BP-SketchAssistant\SketchAssistant\optitrack_setup.ttp"))
                 {
                     optitrackAvailable = true;
-                    connector.StartTracking(getOptiTrackPosition);
+                    connector.StartTracking(GetOptiTrackPosition);
                 }
             }
         }
 
-        [DllImport("user32.dll", EntryPoint = "SetCursorPos")]
-        private static extern bool SetCursorPos(int X, int Y);
+        /**************************/
+        /*** INTERNAL FUNCTIONS ***/
+        /**************************/
 
-        //[DllImport("user32.dll")]
-        //public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
 
-        public enum MouseEventType : int
+        /// <summary>
+        /// Check if the Optitrack trackable is in the drawing plane.
+        /// </summary>
+        /// <param name="optiTrackZ">The real world z coordinates of the trackable.</param>
+        /// <returns>If the trackable is in front of the drawing plane</returns>
+        private bool CheckInsideDrawingZone(float optiTrackZ)
         {
-            LeftDown = 0x02,
-            LeftUp = 0x04,
-            RightDown = 0x08,
-            RightUp = 0x10
+            if (Math.Abs(optiTrackZ) > WARNING_ZONE_BOUNDARY * 2) return false;
+            return true;
         }
 
-
-        /**************************/
-        /*** INTERNAL FUNCTIONS ***/
-        /**************************/
-
+        /// <summary>
+        /// Function that is called by the OptitrackController to pass frames to the model.
+        /// </summary>
+        /// <param name="frame">An Optitrack Frame</param>
+        void GetOptiTrackPosition(OptiTrack.Frame frame)
+        {
+            if (frame.Trackables.Length >= 1)
+            {
+                optiTrackX = frame.Trackables[0].X;
+                optiTrackY = frame.Trackables[0].Y;
+                optiTrackZ = frame.Trackables[0].Z;
+            }
+        }
 
         /// <summary>
         /// Change the status of whether or not the lines are shown.
@@ -205,6 +233,23 @@ namespace SketchAssistantWPF
             }
         }
 
+
+        /// <summary>
+        /// Check if enough distance has been travelled to warrant a vibration.
+        /// </summary>
+        private void CheckPathTraveled()
+        {
+            var a = Math.Abs(previousOptiCursorPosition.X - currentOptiCursorPosition.X);
+            var b = Math.Abs(previousOptiCursorPosition.Y - currentOptiCursorPosition.Y);
+            PathTraveled += Math.Sqrt(Math.Pow(a,2) + Math.Pow(b,2));
+            //Set the Interval of vibrations here
+            if(PathTraveled > 2)
+            {
+                PathTraveled = 0; 
+                //TODO: Activate vibration here
+            }
+        }
+
         /// <summary>
         /// A function that populates the matrixes needed for deletion detection with line data.
         /// </summary>
@@ -256,49 +301,58 @@ namespace SketchAssistantWPF
             return returnSet;
         }
 
-        //TODO: calibrate
-        double OPTITRACK_X_OFFSET = 0.7878;
-        double OPTITRACK_Y_OFFSET = 0.7977;
-        double OPTITRACK_CANVAS_HEIGHT = 1.29;
-        double OPTITRACK_X_SCALE = -0.254 * (((1.816/*size of canvis*/ / 0.0254) * 96) / (1.816));
-        double OPTITRACK_Y_SCALE = 0.254 * (((1.360 / 0.0254) * 96) / (1.360));
         /// <summary>
-        /// converts given point to device-independent pixel
+        /// Converts given point to device-independent pixel.
         /// </summary>
         /// <param name="p">real world coordinate</param>
         /// <returns>The given Point converted to device-independent pixel </returns>
         private Point ConvertTo96thsOfInch(Point p)
         {
+            //The position of the optitrack coordinate system
+            // and sizes of the optitrack tracking area relative to the canvas.
+            double OPTITRACK_X_OFFSET = -0.4854;
+            double OPTITRACK_Y_OFFSET = 0.9;
+            double OPTITRACK_CANVAS_HEIGHT = 1.2;
+            double OPTITRACK_X_SCALE = 0.21 * (((1.8316/*size of canvas*/ / 0.0254) * 96) / (1.8316));
+            double OPTITRACK_Y_SCALE = 0.205 * (((1.2 / 0.0254) * 96) / (1.2));
+            //The coordinates on the display
             double xCoordinate = (p.X - OPTITRACK_X_OFFSET) * OPTITRACK_X_SCALE;
             double yCoordinate = (OPTITRACK_CANVAS_HEIGHT - (p.Y - OPTITRACK_Y_OFFSET)) * OPTITRACK_Y_SCALE;
             return new Point(xCoordinate, yCoordinate);
         }
 
         /// <summary>
-        /// converts given point to pixel, calibrated for video wall in a08
-        /// </summary>
-        /// <param name="p"></param>
-        /// <returns></returns>
-        private Point ConvertToPixel(Point p)
-        {//TODO: realize comments
-            double xCoordinate = (p.X - OPTITRACK_X_OFFSET) * -1 * (/*Anzahl Pixel X-Richtung*/0 / (1.816)) + 0/*1/2 * x-richtung pixel*/; //TODO
-            double yCoordinate = (((OPTITRACK_CANVAS_HEIGHT + 0/*meter von oberer Rand Leinwand zu oberer Rand Bildschirm*/) - (p.Y - OPTITRACK_Y_OFFSET))) * (/*Anzahl Pixel Y-Richtung*/0 / (1.360));
-            return new Point(xCoordinate, yCoordinate);
-        }
-
-        /// <summary>
-        /// Updates the current cursor position of the model.
+        /// Updates the Optitrack coordiantes, aswell as the marker for optitrack.
         /// </summary>
         /// <param name="p">The new cursor position</param>
-        public void SetCurrentFingerPosition(Point p)
+        private void SetCurrentFingerPosition(Point p)
         {
             Point correctedPoint = ConvertTo96thsOfInch(p);
-            if (testing)
+
+
+            if (optiTrackZ < -2.2 * WARNING_ZONE_BOUNDARY && OptiLayer > -1)
             {
-                Console.WriteLine("raw coordinates: " + p.X + ";" + p.Y);
-                Console.WriteLine(correctedPoint.X + "," + correctedPoint.Y);
+                OptiLayer = -1; OptiMovingBack = false;
+                programPresenter.SetOverlayColor("optipoint", Brushes.Yellow);
             }
-            currentCursorPosition = correctedPoint;
+            else if (optiTrackZ > 2 * WARNING_ZONE_BOUNDARY && OptiLayer < 1)
+            {
+                programPresenter.SetOverlayColor("optipoint", Brushes.Red);
+                OptiLayer = 1;
+            }
+            else if(optiTrackZ <= 2 * WARNING_ZONE_BOUNDARY && optiTrackZ >= -2.2 * WARNING_ZONE_BOUNDARY){
+                if(OptiLayer > 0)
+                    OptiMovingBack = true;
+                programPresenter.SetOverlayColor("optipoint", Brushes.Green);
+                OptiLayer = 0;
+            }
+
+            currentOptiCursorPosition = correctedPoint;
+            programPresenter.MoveOptiPoint(currentOptiCursorPosition);
+
+            if (optiCursorPositions.Count > 0) { previousOptiCursorPosition = optiCursorPositions.Dequeue(); }
+            else { previousOptiCursorPosition = currentOptiCursorPosition; }
+            optiCursorPositions.Enqueue(currentOptiCursorPosition);
         }
 
         /********************************************/
@@ -308,13 +362,10 @@ namespace SketchAssistantWPF
         /// <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)
+        public void ResizeEvent(ImageDimension RightCanvas)
         {
-            if (LeftCanvas.Height >= 0 && LeftCanvas.Width >= 0) { leftImageSize = LeftCanvas; }
             if (RightCanvas.Height >= 0 && RightCanvas.Width >= 0) { rightImageSize = RightCanvas; }
-
             RepopulateDeletionMatrixes();
         }
 
@@ -323,6 +374,8 @@ namespace SketchAssistantWPF
         /// </summary>
         public void ResetRightImage()
         {
+            if(currentLine.Count > 0)
+                FinishCurrentLine(true);
             rightLineList.Clear();
             programPresenter.PassLastActionTaken(historyOfActions.Reset());
             programPresenter.ClearRightLines();
@@ -336,29 +389,11 @@ namespace SketchAssistantWPF
         /// <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();
-            /*
-            var workingCanvas = GetEmptyCanvas(width, height);
-            var workingGraph = Graphics.FromImage(workingCanvas);
-            leftLineList = listOfLines;
-            //redrawAss = new RedrawAssistant(leftLineList);
-            //overlayItems = redrawAss.Initialize(markerRadius);
-            //Lines
-            foreach (InternalLine line in leftLineList)
-            {
-                line.DrawLine(workingGraph);
-            }
-            leftImage = workingCanvas;
-            programPresenter.UpdateLeftImage(leftImage);
-            //Set right image to same size as left image and delete linelist
-            DrawEmptyCanvasRight();
-            rightLineList = new List<Tuple<bool, InternalLine>>();
-            */
         }
 
         /// <summary>
@@ -393,7 +428,6 @@ namespace SketchAssistantWPF
                     default:
                         break;
                 }
-                //TODO: For the person implementing overlay: Add check if overlay needs to be added
                 programPresenter.UpdateRightLines(rightLineList);
             }
             RepopulateDeletionMatrixes();
@@ -424,7 +458,6 @@ namespace SketchAssistantWPF
                     default:
                         break;
                 }
-                //TODO: For the person implementing overlay: Add check if overlay needs to be added
                 programPresenter.UpdateRightLines(rightLineList);
                 RepopulateDeletionMatrixes();
             }
@@ -437,6 +470,8 @@ namespace SketchAssistantWPF
         /// <param name="nowDrawing">The new drawingstate of the program</param>
         public void ChangeState(bool nowDrawing)
         {
+            if(inDrawingMode && !nowDrawing && currentLine.Count > 0 && optiTrackInUse)
+                FinishCurrentLine(true);
             inDrawingMode = nowDrawing;
             UpdateUI();
         }
@@ -444,13 +479,21 @@ namespace SketchAssistantWPF
         /// <summary>
         /// The function called by the Presenter to set a variable which describes if OptiTrack is in use
         /// </summary>
-        /// <param name="usingOptiTrack"></param>
+        /// <param name="usingOptiTrack">The status of optitrack button</param>
         public void SetOptiTrack(bool usingOptiTrack)
         {
             optiTrackInUse = usingOptiTrack;
             if (usingOptiTrack && optiTrackX == 0 && optiTrackY == 0 && optiTrackZ == 0)
             {
-                programPresenter.PassMessageToView("Trackable not detected, please check if OptiTrack is activated and Trackable is recognized", "Warning");
+                programPresenter.PassWarning("Trackable not detected, please check if OptiTrack is activated and Trackable is recognized");
+                optiTrackInUse = false;
+                //Disable optipoint
+                programPresenter.SetOverlayStatus("optipoint", false, currentCursorPosition);
+            }
+            else
+            {
+                //Enable optipoint
+                programPresenter.SetOverlayStatus("optipoint", true, currentCursorPosition);
             }
         }
 
@@ -460,7 +503,7 @@ namespace SketchAssistantWPF
         /// <param name="p">The new cursor position</param>
         public void SetCurrentCursorPosition(Point p)
         {
-            if (!optiTrackInUse) currentCursorPosition = p;
+            currentCursorPosition = p;
             mouseDown = programPresenter.IsMousePressed();
         }
 
@@ -469,9 +512,15 @@ namespace SketchAssistantWPF
         /// </summary>
         public void StartNewLine()
         {
-            if (optiTrackInUse || programPresenter.IsMousePressed())
+            mouseDown = true;
+            if (inDrawingMode)
             {
-                if (inDrawingMode)
+                if(optiTrackInUse)
+                {
+                    currentLine.Clear();
+                    currentLine.Add(currentOptiCursorPosition);
+                }
+                else if (programPresenter.IsMousePressed())
                 {
                     currentLine.Clear();
                     currentLine.Add(currentCursorPosition);
@@ -485,13 +534,8 @@ namespace SketchAssistantWPF
         /// <param name="valid">Whether the up event is valid or not</param>
         public void FinishCurrentLine(bool valid)
         {
-            foreach (Point p in currentLine)
-            {
-                if (testing)
-                {
-                    Console.WriteLine(p.X + ";" + p.Y);
-                }
-            }
+
+            mouseDown = false;
             if (valid)
             {
                 if (inDrawingMode && currentLine.Count > 0)
@@ -513,15 +557,24 @@ namespace SketchAssistantWPF
             UpdateUI();
         }
 
-
-        void getOptiTrackPosition(OptiTrack.Frame frame)
+        /// <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 FinishCurrentLine(List<Point> p)
         {
-            if(frame.Trackables.Length > 1)
+            mouseDown = false;
+            if (inDrawingMode && currentLine.Count > 0)
             {
-                optiTrackX = frame.Trackables[0].X;
-                optiTrackY = frame.Trackables[0].Y;
-                optiTrackZ = frame.Trackables[0].Z;
+                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>
@@ -531,61 +584,63 @@ namespace SketchAssistantWPF
         {
             if (cursorPositions.Count > 0) { previousCursorPosition = cursorPositions.Dequeue(); }
             else { previousCursorPosition = currentCursorPosition; }
-            if (optiTrackInUse) //drawing optiTrack
+
+            if(optitrackAvailable)
+                SetCurrentFingerPosition(new Point(optiTrackX, optiTrackY));
+            
+
+            if (optiTrackInUse && inDrawingMode && !OptiMovingBack) // optitrack is being used
             {
-                if (CheckInsideDrawingZone(optiTrackZ))
+                //outside of drawing zone
+                if (!CheckInsideDrawingZone(optiTrackZ))
                 {
-                    SetCurrentFingerPosition(new Point(optiTrackX, optiTrackY));
-                    currentLine.Add(currentCursorPosition);
-                    programPresenter.UpdateCurrentLine(currentLine);
+                    //Check if trackable was in drawing zone last tick & program is in drawing mode-> finish line
+                    if (optiTrackInsideDrawingZone && inDrawingMode) 
+                    {
+                        optiTrackInsideDrawingZone = false;
+                        FinishCurrentLine(true);
+                    }
+                }
+                else //Draw with optitrack, when in drawing zone
+                {
+                    //Optitrack wasn't in the drawing zone last tick -> start a new line
                     if (!optiTrackInsideDrawingZone)
                     {
                         optiTrackInsideDrawingZone = true;
                         StartNewLine();
-                        if (testing)
-                        {
-                            Console.WriteLine("new line begun");
-                        }
                     }
+                    else currentLine.Add(currentOptiCursorPosition);
+                    programPresenter.UpdateCurrentLine(currentLine);
                     if (optiTrackZ > WARNING_ZONE_BOUNDARY)
                     {
-                        armband.pushForward();
+                        wristband.PushForward();
                     }
                     else if (optiTrackZ < -1 * WARNING_ZONE_BOUNDARY)
                     {
-                        armband.pushBackward();
+                        wristband.PushBackward();
                     }
                 }
-                else
-                {
-                    if (optiTrackInsideDrawingZone) //trackable was in drawing zone last tick -> finish line
-                    {
-                        optiTrackInsideDrawingZone = false;
-                        FinishCurrentLine(true);
-                        if (testing)
-                        {
-                            Console.WriteLine("line finished");
-                        }
-                    }
-                }
-                //if (optitrackAvailable) { TODO test and remove
-                projectPointOntoScreen(optiTrackX, optiTrackY);
-                //}
-                cursorPositions.Enqueue(currentCursorPosition);
             }
-            else //drawing normal
+            else if( !optiTrackInUse && inDrawingMode)
             {
+                //drawing without optitrack
                 cursorPositions.Enqueue(currentCursorPosition);
                 if (inDrawingMode && programPresenter.IsMousePressed())
                 {
                     currentLine.Add(currentCursorPosition);
                     //programPresenter.UpdateCurrentLine(currentLine);
                 }
+
             }
-            //Deleting
-            if (!inDrawingMode && programPresenter.IsMousePressed())
+
+            //Deletion mode for optitrack and regular use
+            if (!inDrawingMode)
             {
-                List<Point> uncheckedPoints = GeometryCalculator.BresenhamLineAlgorithm(previousCursorPosition, currentCursorPosition);
+                List<Point> uncheckedPoints = new List<Point>();
+                if (programPresenter.IsMousePressed() && !optiTrackInUse) //without optitrack
+                    uncheckedPoints = GeometryCalculator.BresenhamLineAlgorithm(previousCursorPosition, currentCursorPosition);
+                if(optiTrackInUse && CheckInsideDrawingZone(optiTrackZ)  && !OptiMovingBack) //with optitrack
+                    uncheckedPoints = GeometryCalculator.BresenhamLineAlgorithm(previousOptiCursorPosition, currentOptiCursorPosition);
 
                 foreach (Point currPoint in uncheckedPoints)
                 {
@@ -598,58 +653,12 @@ namespace SketchAssistantWPF
                             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);
                     }
                 }
             }
         }
 
-        private void projectPointOntoScreen(float optiTrackX, float optiTrackY)
-        {
-            Point auxiliaryPoint = ConvertToPixel(new Point(optiTrackX, optiTrackY));
-            SetCursorPos((int)auxiliaryPoint.X, (int)auxiliaryPoint.Y);
-        }
-
-        private bool CheckInsideDrawingZone(float optiTrackZ)
-        {
-            if (Math.Abs(optiTrackZ) > WARNING_ZONE_BOUNDARY * 2) return false;
-            return true;
-        }
-
-        /*
-        /// <summary>
-        /// A helper Function that updates the markerRadius & deletionRadius, considering the size of the canvas.
-        /// </summary>
-        /// <param name="CanvasSize">The size of the canvas</param>
-        public void UpdateSizes(ImageDimension CanvasSize)
-        {
-            if (rightImageWithoutOverlay != null)
-            {
-                int widthImage = rightImageSize.Width;
-                int heightImage = rightImageSize.Height;
-                int widthBox = CanvasSize.Width;
-                int heightBox = CanvasSize.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);
-                deletionRadius = (int)(5 * zoomFactor);
-            }
-        }
-        */
-
         /// <summary>
         /// If there is unsaved progress.
         /// </summary>

+ 213 - 72
SketchAssistant/SketchAssistantWPF/MVP_Presenter.cs

@@ -9,6 +9,7 @@ using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Shapes;
 using OptiTrack;
+using System.Windows.Ink;
 
 namespace SketchAssistantWPF
 {
@@ -26,14 +27,22 @@ namespace SketchAssistantWPF
         /// 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);
+        /// <summary>
+        /// The actual size of the left canvas.
+        /// </summary>
+        ImageDimension canvasSizeLeft = new ImageDimension(0, 0);
+        /// <summary>
+        /// The actual size of the right canvas.
+        /// </summary>
+        ImageDimension canvasSizeRight = new ImageDimension(0, 0);
+        /// <summary>
+        /// A list of line similarities, resulting in the similarity of the whole image.
+        /// </summary>
+        List<double> imageSimilarity = new List<double>();
+        /// <summary>
+        /// The lines in the left canvas.
+        /// </summary>
+        List<InternalLine> leftLines = new List<InternalLine>();
 
         /*******************/
         /*** ENUMERATORS ***/
@@ -41,7 +50,6 @@ namespace SketchAssistantWPF
 
         public enum MouseAction
         {
-            Click,
             Down,
             Up,
             Up_Invalid,
@@ -62,7 +70,6 @@ namespace SketchAssistantWPF
         {
             programView = form;
             programModel = new MVP_Model(this);
-            //Initialize Class Variables
             fileImporter = new FileImporter();
         }
 
@@ -77,57 +84,18 @@ namespace SketchAssistantWPF
         /// <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 load a collection of lines.
-        /// </summary>
-        public void ExamplePictureToolStripMenuItemClick()
-        {
-            var okToContinue = true;
-            if (programModel.HasUnsavedProgress())
-            {
-                okToContinue = programView.ShowWarning("You have unsaved progress. Continue?");
-            }
-            if (okToContinue)
-            {
-                var fileNameTup = programView.openNewDialog("Interactive Sketch-Assistant Drawing|*.isad");
-                if (!fileNameTup.Item1.Equals("") && !fileNameTup.Item2.Equals(""))
-                {
-                    programView.SetToolStripLoadStatus(fileNameTup.Item2);
-                    try
-                    {
-                        Tuple<int, int, List<InternalLine>> values = fileImporter.ParseISADInputFile(fileNameTup.Item1);
-                        programModel.SetLeftLineList(values.Item1, values.Item2, values.Item3);
-
-                        programModel.ResetRightImage();
-                        programModel.CanvasActivated();
-                        programModel.ChangeState(true);
-                        programView.EnableTimer();
-                        ClearRightLines();
-                    }
-                    catch (FileImporterException ex)
-                    {
-                        programView.ShowInfoMessage(ex.ToString(),"");
-                    }
-                    catch (Exception ex)
-                    {
-                        programView.ShowInfoMessage("exception occured while trying to load interactive sketch-assistant drawing file:\n\n" + ex.ToString() + "\n\n" + ex.StackTrace, "");
-                    }
-                }
-            }
+            canvasSizeLeft.ChangeDimension(leftPBS.Item1, leftPBS.Item2);
+            canvasSizeRight.ChangeDimension(rightPBS.Item1, rightPBS.Item2);
+            programModel.ResizeEvent(canvasSizeRight);
         }
 
         /// <summary>
         /// Display a new FileDialog to a svg drawing.
         /// </summary>
-        public void SVGToolStripMenuItemClick()
+        /// <returns>True if loading was a success</returns>
+        public bool SVGToolStripMenuItemClick()
         {
-            var okToContinue = true;
+            var okToContinue = true; bool returnval = false;
             if (programModel.HasUnsavedProgress())
             {
                 okToContinue = programView.ShowWarning("You have unsaved progress. Continue?");
@@ -141,23 +109,27 @@ namespace SketchAssistantWPF
                     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.ResizeEvent(canvasSizeRight);
                         programModel.ResetRightImage();
                         programModel.CanvasActivated();
                         programModel.ChangeState(true);
                         programView.EnableTimer();
                         ClearRightLines();
+                        returnval = true;
                     }
                     catch (FileImporterException ex)
                     {
-                        programView.ShowInfoMessage(ex.ToString(), "");
+                        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, "");
+                        programView.ShowInfoMessage("exception occured while trying to parse svg file:\n\n" + ex.ToString() + "\n\n" + ex.StackTrace);
                     }
                 }
             }
+            return returnval;
         }
 
         /// <summary>
@@ -169,6 +141,24 @@ namespace SketchAssistantWPF
             programModel.ChangeState(NowDrawing);
         }
 
+        /// <summary>
+        /// Gets whether the program is in drawing state or not.
+        /// </summary>
+        /// <returns></returns>
+        public bool GetDrawingState()
+        {
+            return programModel.inDrawingMode;
+        }
+
+        /// <summary>
+        /// Gets whether Optitrack is in use or not.
+        /// </summary>
+        /// <returns>Return optiTrackInUse</returns>
+        public bool GetOptitrackActive()
+        {
+            return programModel.optiTrackInUse;
+        }
+
         /// <summary>
         /// Pass-through function to change the OptiTrack-in-use state of the model
         /// </summary>
@@ -216,7 +206,7 @@ namespace SketchAssistantWPF
             }
             if (okToContinue)
             {
-                programModel.ResizeEvent(CanvasSizeLeft, CanvasSizeRight);
+                programModel.ResizeEvent(canvasSizeRight);
                 programModel.ResetRightImage();
                 programModel.CanvasActivated();
                 programModel.ChangeState(true);
@@ -246,22 +236,30 @@ namespace SketchAssistantWPF
         /// 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>
-        public void MouseEvent(MouseAction mouseAction)
+        /// <param name="strokes">The Strokes.</param>
+        public void MouseEvent(MouseAction mouseAction, StrokeCollection strokes)
         {
+
             if (!programModel.optiTrackInUse)
             {
                 switch (mouseAction)
                 {
-                    case MouseAction.Click:
-                        programModel.StartNewLine();
-                        programModel.Tick();
-                        programModel.FinishCurrentLine(true);
-                        break;
                     case MouseAction.Down:
                         programModel.StartNewLine();
                         break;
                     case MouseAction.Up:
-                        programModel.FinishCurrentLine(true);
+                        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.FinishCurrentLine(points);
+                        }
+                        else
+                        {
+                            programModel.FinishCurrentLine(true);
+                        }
                         break;
                     case MouseAction.Up_Invalid:
                         programModel.FinishCurrentLine(false);
@@ -304,6 +302,8 @@ namespace SketchAssistantWPF
         {
             programView.RemoveAllRightLines();
             rightPolyLines = new Dictionary<int, Shape>();
+            //Reset the similarity display
+            UpdateSimilarityScore(Double.NaN);
         }
 
         /// <summary>
@@ -333,6 +333,20 @@ namespace SketchAssistantWPF
                 }
                 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>
@@ -350,6 +364,8 @@ namespace SketchAssistantWPF
             }
             programView.SetCanvasState("LeftCanvas", true);
             programView.SetCanvasState("RightCanvas", true);
+
+            leftLines = lines;
         }
 
         /// <summary>
@@ -421,19 +437,20 @@ namespace SketchAssistantWPF
         /// Pass-trough function to display an info message in the view.
         /// </summary>
         /// <param name="msg">The message.</param>
-        /// <param name="caption">The caption.</param>
-        public void PassMessageToView(String msg, String caption)
+        public void PassMessageToView(String msg)
         {
-            programView.ShowInfoMessage(msg, caption);
+            programView.ShowInfoMessage(msg);
         }
 
         /// <summary>
         /// Pass-through function to desplay an Warning message in the view
         /// </summary>
-        /// <param name="msg"></param>
-        public void PassWarning(String msg)
+        /// <param name="msg">The message.</param>
+
+        /// <returns>True if the user confirms (Yes), negative if he doesn't (No)</returns>
+        public bool PassWarning(String msg)
         {
-            programView.ShowWarning(msg);
+            return programView.ShowWarning(msg);
         }
 
         /// <summary>
@@ -457,13 +474,137 @@ namespace SketchAssistantWPF
         public void PassOptiTrackMessage(String stringToPass)
         {
             programView.SetOptiTrackText(stringToPass);
-            //programView.SetOptiTrackText("X: ");// + x + "Y: " + y + "Z: " + z);
+        }
+
+        /// <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 color of an overlay item.
+        /// </summary>
+        /// <param name="name">The name of the item in the overlay dictionary</param>
+        /// <param name="color">The color of the item</param>
+        public void SetOverlayColor(String name, Brush color)
+        {
+            Shape shape = new Ellipse();
+            if(((MainWindow)programView).overlayDictionary.ContainsKey(name))
+            {
+                shape = ((MainWindow)programView).overlayDictionary[name];
+                
+                if(name.Substring(0, 7).Equals("dotLine"))
+                    shape.Stroke = color;
+                else
+                    shape.Fill = color;
+            }
+        }
+
+        /// <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;
+            Point point = ConvertRightCanvasCoordinateToOverlay(position);
+            
+            switch (name)
+            {
+                case "optipoint":
+                    shape = ((MainWindow)programView).overlayDictionary["optipoint"];
+                    visibility = 0.75; 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;
+            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>

+ 10 - 1
SketchAssistant/SketchAssistantWPF/MVP_View.cs

@@ -5,6 +5,7 @@ using System.Text;
 using System.Threading.Tasks;
 using System.Windows;
 using System.Windows.Controls;
+using System.Windows.Ink;
 using System.Windows.Shapes;
 
 namespace SketchAssistantWPF
@@ -87,7 +88,7 @@ namespace SketchAssistantWPF
         /// 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, String caption);
+        void ShowInfoMessage(String message);
 
         /// <summary>
         /// Shows a warning box with the given message (Yes/No Buttons)and returns true if the user aknowledges it.
@@ -126,5 +127,13 @@ namespace SketchAssistantWPF
         /// </summary>
         /// <param name="message">The new contents</param>
         void SetOptiTrackText(String 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>
+        void SetImageSimilarityText(string message);
     }
 }

+ 29 - 29
SketchAssistant/SketchAssistantWPF/MainWindow.xaml

@@ -1,32 +1,26 @@
-<Window x:Class="SketchAssistantWPF.MainWindow" 
+<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>
+    <Grid x:Name="RootGrid">
         <Grid.ColumnDefinitions>
-            <ColumnDefinition Width="auto"/>
-            <ColumnDefinition Width="auto"/>
-            <ColumnDefinition Width="170*"/>
-            <ColumnDefinition Width="55*"/>
-            <ColumnDefinition Width="225*"/>
             <ColumnDefinition Width="5"/>
-            <ColumnDefinition Width="226*"/>
-            <ColumnDefinition Width="225*"/>
-            <ColumnDefinition Width="auto"/>
-            <ColumnDefinition Width="auto"/>
+            <ColumnDefinition Width="*"/>
+            <ColumnDefinition Width="5"/>
+            <ColumnDefinition Width="*"/>
+            <ColumnDefinition Width="5"/>
         </Grid.ColumnDefinitions>
         <Grid.RowDefinitions>
             <RowDefinition Height="auto"/>
             <RowDefinition Height="*"/>
             <RowDefinition Height="auto"/>
         </Grid.RowDefinitions>
-        <ToolBar x:Name="MenuToolbar" Grid.Column="0" Grid.ColumnSpan="5" Grid.Row="0" Background="LightGray">
-            <Menu Background="LightGray">
+        <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="ISADMenuButton" Header="Load Template (.isad)" Click="ISADMenuItem_Click"/>
-                    <MenuItem x:Name="SVGMenuButton" Header="Load Template (.svg)" Click="SVGMenuItem_Click"/>
+                    <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"/>
@@ -40,10 +34,11 @@
                     </MenuItem>
                 </MenuItem>
             </Menu>
-        </ToolBar>
-        <!-- All Icons in the Toolbar taken from openclipart.org -->
-        <ToolBar x:Name="DrawingToolBar" Grid.Column="6" Grid.Row="0" Grid.ColumnSpan="2" Background="LightGray">
-            <Button x:Name="CanvasButton" ToolTip="Create a new Canvas" Click="CanvasButton_Click">
+        </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>
@@ -72,7 +67,7 @@
                     </Rectangle.Fill>
                 </Rectangle>
             </Button>
-            <ToggleButton x:Name="DrawButton" ToolTip="Enter Drawing Mode" Click="DrawButton_Click">
+            <ToggleButton x:Name="DrawButton" ToolTip="Enter Drawing Mode" Click="DrawButton_Click" BorderThickness="0">
                 <Rectangle Width="30" Height="30">
                     <Rectangle.Fill>
                         <DrawingBrush>
@@ -87,7 +82,7 @@
                     </Rectangle.Fill>
                 </Rectangle>
             </ToggleButton>
-            <ToggleButton x:Name="DeleteButton" ToolTip="Enter Deletion Mode" Click="DeleteButton_Click">
+            <ToggleButton x:Name="DeleteButton" ToolTip="Enter Deletion Mode" Click="DeleteButton_Click" BorderThickness="0">
                 <Rectangle Width="30" Height="30">
                     <Rectangle.Fill>
                         <DrawingBrush>
@@ -129,7 +124,7 @@
                     </Rectangle.Fill>
                 </Rectangle>
             </ToggleButton>
-            <Button x:Name="UndoButton" ToolTip="Undo the last action" Click="UndoButton_Click">
+            <Button x:Name="UndoButton" ToolTip="Undo the last action" Click="UndoButton_Click" BorderThickness="0">
                 <Rectangle Width="30" Height="30">
                     <Rectangle.Fill>
                         <DrawingBrush>
@@ -151,7 +146,7 @@
                     </Rectangle.Fill>
                 </Rectangle>
             </Button>
-            <Button x:Name="RedoButton" ToolTip="Redo the last undone action" Click="RedoButton_Click">
+            <Button x:Name="RedoButton" ToolTip="Redo the last undone action" Click="RedoButton_Click" BorderThickness="0">
                 <Rectangle Width="30" Height="30">
                     <Rectangle.Fill>
                         <DrawingBrush>
@@ -175,7 +170,7 @@
                     </Rectangle.Fill>
                 </Rectangle>
             </Button>
-            <ToggleButton x:Name="DrawWithOptiButton" ToolTip="Enter Drawing Mode (OptiTrack)" Click="DrawWithOptiButton_Click">
+            <ToggleButton x:Name="DrawWithOptiButton" ToolTip="Enter Drawing Mode (OptiTrack)" Click="DrawWithOptiButton_Click" BorderThickness="0">
                 <Rectangle Width="30" Height="30">
                     <Rectangle.Fill>
                         <DrawingBrush>
@@ -216,11 +211,12 @@
                     </Rectangle.Fill>
                 </Rectangle>
             </ToggleButton>
-        </ToolBar>
-        <local:CustomCanvas x:Name="LeftCanvas" Background="SlateGray" Grid.Column="2" Grid.Row="1" Height="auto" Grid.ColumnSpan="3"/>
-        <Canvas Name="CanvasSeperator" Grid.Column="5" Grid.Row="1" Background="LightGray" />
-        <InkCanvas x:Name="RightCanvas" Background="SlateGray" Grid.Column="6" Grid.Row="1" Height="auto" PreviewMouseDown="RightCanvas_MouseDown" MouseUp="RightCanvas_MouseUp" MouseMove="RightCanvas_MouseMove" Grid.ColumnSpan="2" StrokeCollected="RightCanvas_StrokeCollection" EditingMode="None"/>
-
+        </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">
@@ -229,7 +225,11 @@
                 <TextBox Name="LastActionBox" Text="none" Background="LightGray"/>
                 <Separator/>
                 <TextBox Name="OptiTrackBox" 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>

+ 130 - 57
SketchAssistant/SketchAssistantWPF/MainWindow.xaml.cs

@@ -20,6 +20,7 @@ using System.Windows.Navigation;
 using System.Windows.Shapes;
 using System.Windows.Threading;
 using System.Windows.Ink;
+using System.Windows.Media.Effects;
 
 namespace SketchAssistantWPF
 {
@@ -48,9 +49,11 @@ namespace SketchAssistantWPF
             //  DispatcherTimer setup
             dispatcherTimer = new DispatcherTimer(DispatcherPriority.Render);
             dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
-            dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 10);
+            dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 5);
             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
@@ -92,7 +95,15 @@ namespace SketchAssistantWPF
         /// <summary>
         /// Stores Lines drawn on RightCanvas.
         /// </summary>
-        StrokeCollection strokeCollection = new StrokeCollection();
+        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 ***/
@@ -113,7 +124,6 @@ namespace SketchAssistantWPF
         public void RightCanvas_StrokeCollection(object sender, InkCanvasStrokeCollectedEventArgs e)
         {
             strokeCollection.Add(e.Stroke);
-            System.Diagnostics.Debug.WriteLine(strokeCollection.Count);
         }
 
         /// <summary>
@@ -121,7 +131,7 @@ namespace SketchAssistantWPF
         /// </summary>
         private void RedoButton_Click(object sender, RoutedEventArgs e)
         {
-            ProgramPresenter.Redo();
+            if (!IsMousePressed()) ProgramPresenter.Redo();
         }
 
         /// <summary>
@@ -129,7 +139,7 @@ namespace SketchAssistantWPF
         /// </summary>
         private void UndoButton_Click(object sender, RoutedEventArgs e)
         {
-            ProgramPresenter.Undo();
+            if (!IsMousePressed()) ProgramPresenter.Undo();
         }
 
         /// <summary>
@@ -146,7 +156,6 @@ namespace SketchAssistantWPF
         /// </summary>
         private void DrawButton_Click(object sender, RoutedEventArgs e)
         {
-            ProgramPresenter.ChangeOptiTrack(false);
             ProgramPresenter.ChangeState(true);
             RightCanvas.EditingMode = InkCanvasEditingMode.Ink;
         }
@@ -156,9 +165,19 @@ namespace SketchAssistantWPF
         /// </summary>
         private void DrawWithOptiButton_Click(object sender, RoutedEventArgs e)
         {
-            ProgramPresenter.ChangeOptiTrack(true);
-            ProgramPresenter.ChangeState(true);
-            RightCanvas.EditingMode = InkCanvasEditingMode.Ink;
+            if (ProgramPresenter.GetOptitrackActive())
+            {
+                ProgramPresenter.ChangeOptiTrack(false);
+                if (ProgramPresenter.GetDrawingState())
+                    RightCanvas.EditingMode = InkCanvasEditingMode.Ink;
+                else
+                    RightCanvas.EditingMode = InkCanvasEditingMode.EraseByStroke;
+            }
+            else
+            {
+                ProgramPresenter.ChangeOptiTrack(true);
+                RightCanvas.EditingMode = InkCanvasEditingMode.None;
+            }
         }
 
         /// <summary>
@@ -166,8 +185,7 @@ namespace SketchAssistantWPF
         /// </summary>
         private void RightCanvas_MouseDown(object sender, MouseButtonEventArgs e)
         {
-            ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Down);
-            //System.Diagnostics.Debug.WriteLine("ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Down);");
+            ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Down, strokeCollection);
         }
 
         /// <summary>
@@ -175,18 +193,41 @@ namespace SketchAssistantWPF
         /// </summary>
         private void RightCanvas_MouseUp(object sender, MouseButtonEventArgs e)
         {
-            if (strokeCollection.Count == 0)
+            if (ProgramPresenter.GetDrawingState())
             {
-                ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up_Invalid);
+                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);
+                }
             }
-            else
+        }
+
+        /// <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)
             {
-                ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up);
-                RightCanvas.Strokes.RemoveAt(0);
-                strokeCollection.RemoveAt(0);
-                System.Diagnostics.Debug.WriteLine(strokeCollection.Count);
+                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);
+                }
             }
-            //System.Diagnostics.Debug.WriteLine("ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up);");
         }
 
         /// <summary>
@@ -195,7 +236,6 @@ namespace SketchAssistantWPF
         private void RightCanvas_MouseMove(object sender, MouseEventArgs e)
         {
             ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Move, e.GetPosition(RightCanvas));
-            //System.Diagnostics.Debug.WriteLine("new Point(" + ((int)e.GetPosition(RightCanvas).X).ToString() + "," + ((int)e.GetPosition(RightCanvas).Y).ToString() + "), ");
         }
 
         /// <summary>
@@ -216,25 +256,21 @@ namespace SketchAssistantWPF
         private void dispatcherTimer_Tick(object sender, EventArgs e)
         {
             ProgramPresenter.Tick();
-            //System.Diagnostics.Debug.WriteLine("ProgramPresenter.Tick();");
         }
 
-        /// <summary>
-        /// Import button for .isad drawing, will open an OpenFileDialog
-        /// </summary>
-        private void ISADMenuItem_Click(object sender, RoutedEventArgs e)
-        {
-            ProgramPresenter.ExamplePictureToolStripMenuItemClick();
-        }
-
-
         /// <summary>
         /// Import button for .svg file, will open an OpenFileDialog
         /// </summary>
         private void SVGMenuItem_Click(object sender, RoutedEventArgs e)
         {
-            ProgramPresenter.SVGToolStripMenuItemClick();
+            if (ProgramPresenter.SVGToolStripMenuItemClick())
+            {
+                ProgramPresenter.NewCanvas();
+                RightCanvas.EditingMode = InkCanvasEditingMode.Ink;
+                RightCanvas.Strokes.Clear();
+            }
         }
+
         /*************************/
         /*** PRESENTER -> VIEW ***/
         /*************************/
@@ -389,6 +425,17 @@ namespace SketchAssistantWPF
             OptiTrackBox.Text = message;
         }
 
+        /// 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>
@@ -476,16 +523,11 @@ namespace SketchAssistantWPF
 
         /// <summary>
         /// shows the given info message in a popup and asks the user to aknowledge it
-        /// if caption is "Warning" add MessageBoxImage.Warning to the pop up window
         /// </summary>
         /// <param name="message">the message to show</param>
-        public void ShowInfoMessage(string message, string caption)
-        {   
-            if(caption == "Warning")
-            {
-                MessageBox.Show(message, caption, MessageBoxButton.OK, MessageBoxImage.Warning);
-            }
-            else MessageBox.Show(message, caption);
+        public void ShowInfoMessage(string message)
+        {
+            MessageBox.Show(message);
         }
 
         /// <summary>
@@ -509,24 +551,12 @@ namespace SketchAssistantWPF
             switch (canvasName)
             {
                 case ("LeftCanvas"):
-                    if (active)
-                    {
-                        LeftCanvas.Background = Brushes.White;
-                    }
-                    else
-                    {
-                        LeftCanvas.Background = Brushes.SlateGray;
-                    }
+                    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;
-                    }
+                    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");
@@ -537,7 +567,48 @@ namespace SketchAssistantWPF
         /*** 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; EndPointOverlay.Width = markerRadius * 2;
+            EndPointOverlay.Fill = Brushes.Green;
+            EndPointOverlay.Effect = effect;
+            overlayDictionary.Add("endpoint", EndPointOverlay);
+            //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;
+            }
+        }
 
         /// <summary>
         /// Sends inputs to the presenter simulating drawing, used for testing and debugging.
@@ -575,6 +646,8 @@ namespace SketchAssistantWPF
             Debug(4);
         }
 
+
+
         /// <summary>
         /// A function which simulates canvas input for debugging.
         /// </summary>
@@ -605,7 +678,7 @@ namespace SketchAssistantWPF
             debugRunning = true;
             ProgramPresenter.Tick(); await Task.Delay(10);
             ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Move, start);
-            ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Down); await Task.Delay(10);
+            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]);
@@ -616,7 +689,7 @@ namespace SketchAssistantWPF
                     await Task.Delay(1);
                 }
             }
-            ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up); await Task.Delay(1);
+            ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up, strokeCollection); await Task.Delay(1);
             debugRunning = false;
             dispatcherTimer.Start();
         }

+ 1 - 4
SketchAssistant/SketchAssistantWPF/SketchAction.cs

@@ -72,7 +72,7 @@ namespace SketchAssistantWPF
         /// <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 SketchAssistantWPF
                         returnString = "Several Lines were deleted.";
                     }
                     break;
-                default:
-                    returnString = "There is no information available for this action.";
-                    break;
             }
             return returnString;
         }

+ 13 - 4
SketchAssistant/SketchAssistantWPF/SketchAssistantWPF.csproj

@@ -1,6 +1,6 @@
 <?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.14\build\netstandard2.0\ReportGenerator.props" Condition="Exists('..\packages\ReportGenerator.4.0.14\build\netstandard2.0\ReportGenerator.props')" />
+  <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>
@@ -17,6 +17,7 @@
     <Deterministic>true</Deterministic>
     <NuGetPackageImportStamp>
     </NuGetPackageImportStamp>
+    <IsWebBootstrapper>false</IsWebBootstrapper>
     <PublishUrl>publish\</PublishUrl>
     <Install>true</Install>
     <InstallFrom>Disk</InstallFrom>
@@ -29,7 +30,6 @@
     <MapFileExtensions>true</MapFileExtensions>
     <ApplicationRevision>0</ApplicationRevision>
     <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
-    <IsWebBootstrapper>false</IsWebBootstrapper>
     <UseApplicationTrust>false</UseApplicationTrust>
     <BootstrapperEnabled>true</BootstrapperEnabled>
   </PropertyGroup>
@@ -72,6 +72,9 @@
     <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
     <Prefer32Bit>true</Prefer32Bit>
   </PropertyGroup>
+  <PropertyGroup>
+    <ApplicationIcon>nicubunu-Quill.ico</ApplicationIcon>
+  </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="System.Data" />
@@ -88,6 +91,9 @@
     <Reference Include="WindowsBase" />
     <Reference Include="PresentationCore" />
     <Reference Include="PresentationFramework" />
+    <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>
     <ApplicationDefinition Include="App.xaml">
@@ -95,7 +101,7 @@
       <SubType>Designer</SubType>
     </ApplicationDefinition>
     <Compile Include="ActionHistory.cs" />
-    <Compile Include="Armband.cs" />
+    <Compile Include="Wristband.cs" />
     <Compile Include="CustomCanvas.cs" />
     <Compile Include="DebugData.cs" />
     <Compile Include="FileImporter.cs" />
@@ -173,11 +179,14 @@
   <ItemGroup>
     <WCFMetadata Include="Connected Services\" />
   </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.14\build\netstandard2.0\ReportGenerator.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\ReportGenerator.4.0.14\build\netstandard2.0\ReportGenerator.props'))" />
+    <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>

+ 24 - 0
SketchAssistant/SketchAssistantWPF/Wristband.cs

@@ -0,0 +1,24 @@
+using System;
+
+namespace SketchAssistantWPF
+{
+    public class Wristband
+    {
+        
+        /// <summary>
+        /// Function to call when the wristband should push forwards.
+        /// </summary>
+        public void PushForward()
+        {
+            Console.WriteLine("FORWARD_PUSH");
+        }
+
+        /// <summary>
+        /// Function to call when the wristband should push backwards.
+        /// </summary>
+        public void PushBackward()
+        {
+            Console.WriteLine("BACKWARD_PUSH");
+        }
+    }
+}

BIN
SketchAssistant/SketchAssistantWPF/nicubunu-Quill.ico


BIN
SketchAssistant/SketchAssistantWPF/optitrack_setup.ttp


+ 3 - 2
SketchAssistant/SketchAssistantWPF/packages.config

@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="Microsoft.TestPlatform" version="16.0.0" targetFramework="net461" />
+  <package id="InputSimulator" version="1.0.4.0" targetFramework="net461" />
+  <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.14" targetFramework="net461" />
+  <package id="ReportGenerator" version="4.0.15" targetFramework="net461" />
 </packages>

+ 472 - 138
SketchAssistant/WhiteTests/UITest.cs

@@ -14,8 +14,12 @@ using TestStack.White.UIItems.WindowStripControls;
 using TestStack.White.UIItems.MenuItems;
 using System.Collections.Generic;
 using System.Text.RegularExpressions;
-//using WindowsInput;
-//using WindowsInput.Native;
+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
 {
@@ -100,37 +104,186 @@ namespace WhiteTests
             }
 
             ProcessStartInfo processStart = new ProcessStartInfo(files[0], "-debug");
+            /*
+            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(30);
+            string[] files = Directory.GetFiles(getSketchAssistantDirectory() + @"\whitelisted", "*.svg", SearchOption.AllDirectories);
+            Thread.Sleep(30);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("LoadMenuButton")).Click();
+            Thread.Sleep(30);
+            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(30);
+            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(30);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(30);
+            inputSimulator.Mouse.MoveMouseBy(100, 100);
+            inputSimulator.Mouse.LeftButtonDown();
+            Thread.Sleep(30);
+            inputSimulator.Mouse.MoveMouseBy(100, 100);
+            Thread.Sleep(30);
+            inputSimulator.Mouse.LeftButtonUp();
+            Thread.Sleep(30);
+            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(30);
+            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(30);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(30);
+            inputSimulator.Mouse.MoveMouseBy(0, 200);
+            inputSimulator.Mouse.LeftButtonDown();
+            Thread.Sleep(30);
+            inputSimulator.Mouse.MoveMouseBy(500, 300);
+            Thread.Sleep(30);
+            inputSimulator.Mouse.LeftButtonUp();
+            Thread.Sleep(30);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(30);
+            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(30);
+            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(30);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(30);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("DrawButton")).Click();
+            Thread.Sleep(30);
+            inputSimulator.Mouse.LeftButtonDown();
+            inputSimulator.Mouse.MoveMouseBy(0, 200);
+            Thread.Sleep(30);
+            inputSimulator.Mouse.MoveMouseBy(500, 300);
+            Thread.Sleep(30);
+            inputSimulator.Mouse.LeftButtonUp();
+            Thread.Sleep(30);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(30);
+            inputSimulator.Mouse.MoveMouseBy(-1000, 0);
+            Thread.Sleep(30);
+            inputSimulator.Mouse.LeftButtonDown();
+            inputSimulator.Mouse.MoveMouseBy(1000, 0);
+            inputSimulator.Mouse.LeftButtonUp();
+            Thread.Sleep(30);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Close();
+        }
+
         [TestMethod]
-        [TestCategory("bla")]
+        [TestCategory("DirectInput")]
+        public void PointsOnCanvasSimilarityTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(30);
+            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(30);
+            inputSimulator.Mouse.LeftButtonUp();
+            Thread.Sleep(30);
+            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(30);
+            inputSimulator.Mouse.LeftButtonDown();
+            Thread.Sleep(30);
+            inputSimulator.Mouse.LeftButtonUp();
+            Thread.Sleep(30);
+            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(30);
+            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(30);
+            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);
+            Thread.Sleep(30);
             Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
             mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
             mainWindow.Close();
         }
 
         [TestMethod]
-        [TestCategory("bla")]
+        [TestCategory("DebugInput")]
         public void DrawLineTest()
         {
             Window mainWindow = setupapp();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
             mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             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);
+            Thread.Sleep(30);
             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             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());
@@ -138,30 +291,31 @@ namespace WhiteTests
         }
 
         [TestMethod]
+        [TestCategory("DebugInput")]
         public void DeleteLineTest()
         {
             Window mainWindow = setupapp();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
             mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             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);
+            Thread.Sleep(30);
             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             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);
+            Thread.Sleep(30);
             mainWindow.Get<Button>(SearchCriteria.ByAutomationId("DeleteButton")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             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());
@@ -169,118 +323,120 @@ namespace WhiteTests
         }
 
         [TestMethod]
-        [TestCategory("bla")]
+        [TestCategory("DebugInput")]
         public void UndoTest()
         {
             Window mainWindow = setupapp();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
             mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             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);
+            Thread.Sleep(30);
             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             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);
+            Thread.Sleep(30);
             mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
             mainWindow.Close();
         }
 
         [TestMethod]
-        [TestCategory("bla")]
+        [TestCategory("DebugInput")]
         public void RedoTest()
         {
             Window mainWindow = setupapp();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
             mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             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);
+            Thread.Sleep(30);
             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             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);
+            Thread.Sleep(30);
             mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             mainWindow.Get<Button>(SearchCriteria.ByAutomationId("RedoButton")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
             mainWindow.Close();
         }
 
         [TestMethod]
-        public void DrawSeveralLines()
+        [TestCategory("DebugInput")]
+        public void DrawSeveralLinesTest()
         {
             Window mainWindow = setupapp();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
             mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             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);
+            Thread.Sleep(30);
             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             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);
+            Thread.Sleep(30);
             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             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);
+            Thread.Sleep(30);
             mainWindow.Close();
         }
 
         [TestMethod]
-        public void DeleteSeveralLines()
+        [TestCategory("DebugInput")]
+        public void DeleteSeveralLinesTest()
         {
             Window mainWindow = setupapp();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
             mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             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);
+            Thread.Sleep(30);
             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             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);
+            Thread.Sleep(30);
             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             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);
+            Thread.Sleep(30);
             mainWindow.Get<Button>(SearchCriteria.ByAutomationId("DeleteButton")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugThree")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             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());
@@ -288,210 +444,286 @@ namespace WhiteTests
         }
 
         [TestMethod]
-        public void UndoSeveralLines()
+        [TestCategory("DebugInput")]
+        public void UndoSeveralLinesTest()
         {
             Window mainWindow = setupapp();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
             mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
-            Thread.Sleep(20);
+            Thread.Sleep(30);
             Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());<