Browse Source

Merge branch 'develop' into userstory20
Fixed an error where normal drawing mode wasn't reactivated when
no trackable was detected.

Martin Edlund 5 years ago
parent
commit
caf72c4d19

+ 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

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

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>

+ 43 - 130
SketchAssistant/SketchAssistantWPF/MVP_Model.cs

@@ -23,9 +23,15 @@ namespace SketchAssistantWPF
         /// History of Actions
         /// </summary>
         ActionHistory historyOfActions;
+        /// <summary>
+        /// The assistant responsible for the redraw mode
+        /// </summary>
+
 
         OptiTrackConnector connector;
 
+
+
         /***********************/
         /*** CLASS VARIABLES ***/
         /***********************/
@@ -43,10 +49,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;
@@ -67,10 +69,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;
@@ -93,7 +91,7 @@ namespace SketchAssistantWPF
         /// <summary>
         /// Indicates whether or not the canvas on the right side is active.
         /// </summary>
-        public bool canvasActive { get; set; }
+        public bool canvasActive {get; set;}
         /// <summary>
         /// Indicates if there is a graphic loaded in the left canvas.
         /// </summary>
@@ -132,18 +130,19 @@ namespace SketchAssistantWPF
         bool testing = false;//TODO: remove after finishing userstory
 
 
-        Image rightImageWithoutOverlay;
         /// <summary>
         /// Whether or not the mouse is pressed.
         /// </summary>
         private bool mouseDown;
 
         List<InternalLine> leftLineList;
-
+        
         List<Tuple<bool, InternalLine>> rightLineList;
 
         List<Point> currentLine = new List<Point>();
 
+
+
         public MVP_Model(MVP_Presenter presenter)
         {
             programPresenter = presenter;
@@ -183,7 +182,6 @@ namespace SketchAssistantWPF
             RightUp = 0x10
         }
 
-
         /**************************/
         /*** INTERNAL FUNCTIONS ***/
         /**************************/
@@ -312,9 +310,8 @@ namespace SketchAssistantWPF
         /// <param name="RightCanvas">The size of the right canvas.</param>
         public void ResizeEvent(ImageDimension LeftCanvas, ImageDimension RightCanvas)
         {
-            if (LeftCanvas.Height >= 0 && LeftCanvas.Width >= 0) { leftImageSize = LeftCanvas; }
-            if (RightCanvas.Height >= 0 && RightCanvas.Width >= 0) { rightImageSize = RightCanvas; }
-
+            if(LeftCanvas.Height >= 0 && LeftCanvas.Width>= 0) { leftImageSize = LeftCanvas; }
+            if(RightCanvas.Height >= 0 && RightCanvas.Width >= 0) { rightImageSize = RightCanvas; }
             RepopulateDeletionMatrixes();
         }
 
@@ -342,23 +339,6 @@ namespace SketchAssistantWPF
             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 +373,6 @@ namespace SketchAssistantWPF
                     default:
                         break;
                 }
-                //TODO: For the person implementing overlay: Add check if overlay needs to be added
                 programPresenter.UpdateRightLines(rightLineList);
             }
             RepopulateDeletionMatrixes();
@@ -450,7 +429,8 @@ namespace SketchAssistantWPF
             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;
             }
         }
 
@@ -469,6 +449,7 @@ namespace SketchAssistantWPF
         /// </summary>
         public void StartNewLine()
         {
+            mouseDown = true;
             if (optiTrackInUse || programPresenter.IsMousePressed())
             {
                 if (inDrawingMode)
@@ -492,6 +473,7 @@ namespace SketchAssistantWPF
                     Console.WriteLine(p.X + ";" + p.Y);
                 }
             }
+            mouseDown = false;
             if (valid)
             {
                 if (inDrawingMode && currentLine.Count > 0)
@@ -513,10 +495,31 @@ namespace SketchAssistantWPF
             UpdateUI();
         }
 
+        /// <summary>
+        /// Finish the current Line, when the pressed Mouse is released.
+        /// Overload that is used to pass a list of points to be used when one is available.
+        /// </summary>
+        /// <param name="p">The list of points</param>
+        public void FinishCurrentLine(List<Point> p)
+        {
+            mouseDown = false;
+            if (inDrawingMode && currentLine.Count > 0)
+            {
+                InternalLine newLine = new InternalLine(p, rightLineList.Count);
+                rightLineList.Add(new Tuple<bool, InternalLine>(true, newLine));
+                newLine.PopulateMatrixes(isFilledMatrix, linesMatrix);
+                programPresenter.PassLastActionTaken(historyOfActions.AddNewAction(new SketchAction(SketchAction.ActionType.Draw, newLine.GetID())));
+                programPresenter.UpdateRightLines(rightLineList);
+                currentLine.Clear();
+            }
+            UpdateUI();
+        }
+
+
 
         void getOptiTrackPosition(OptiTrack.Frame frame)
         {
-            if(frame.Trackables.Length >= 1)
+            if (frame.Trackables.Length >= 1)
             {
                 optiTrackX = frame.Trackables[0].X;
                 optiTrackY = frame.Trackables[0].Y;
@@ -531,62 +534,17 @@ namespace SketchAssistantWPF
         {
             if (cursorPositions.Count > 0) { previousCursorPosition = cursorPositions.Dequeue(); }
             else { previousCursorPosition = currentCursorPosition; }
-            if (optiTrackInUse) //drawing optiTrack
-            {
-                if (CheckInsideDrawingZone(optiTrackZ))
-                {
-                    SetCurrentFingerPosition(new Point(optiTrackX, optiTrackY));
-                    currentLine.Add(currentCursorPosition);
-                    programPresenter.UpdateCurrentLine(currentLine);
-                    if (!optiTrackInsideDrawingZone)
-                    {
-                        optiTrackInsideDrawingZone = true;
-                        StartNewLine();
-                        if (testing)
-                        {
-                            Console.WriteLine("new line begun");
-                        }
-                    }
-                    if (optiTrackZ > WARNING_ZONE_BOUNDARY)
-                    {
-                        armband.pushForward();
-                    }
-                    else if (optiTrackZ < -1 * WARNING_ZONE_BOUNDARY)
-                    {
-                        armband.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
+            cursorPositions.Enqueue(currentCursorPosition);
+            //Drawing
+            if (inDrawingMode && programPresenter.IsMousePressed())
             {
-                cursorPositions.Enqueue(currentCursorPosition);
-                if (inDrawingMode && programPresenter.IsMousePressed())
-                {
-                    currentLine.Add(currentCursorPosition);
-                    //programPresenter.UpdateCurrentLine(currentLine);
-                }
+                currentLine.Add(currentCursorPosition);
+                //programPresenter.UpdateCurrentLine(currentLine);
             }
             //Deleting
             if (!inDrawingMode && programPresenter.IsMousePressed())
             {
                 List<Point> uncheckedPoints = GeometryCalculator.BresenhamLineAlgorithm(previousCursorPosition, currentCursorPosition);
-
                 foreach (Point currPoint in uncheckedPoints)
                 {
                     HashSet<int> linesToDelete = CheckDeletionMatrixesAroundPoint(currPoint, deletionRadius);
@@ -605,51 +563,6 @@ namespace SketchAssistantWPF
             }
         }
 
-        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>

+ 72 - 57
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
 {
@@ -35,13 +36,16 @@ namespace SketchAssistantWPF
 
         ImageDimension ImageSizeRight = new ImageDimension(0, 0);
 
+        List<double> ImageSimilarity = new List<double>();
+
+        List<InternalLine> LeftLines = new List<InternalLine>();
+
         /*******************/
         /*** ENUMERATORS ***/
         /*******************/
 
         public enum MouseAction
         {
-            Click,
             Down,
             Up,
             Up_Invalid,
@@ -83,51 +87,13 @@ namespace SketchAssistantWPF
             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, "");
-                    }
-                }
-            }
-        }
-
         /// <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 +107,26 @@ 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.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>
@@ -246,22 +215,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 +281,8 @@ namespace SketchAssistantWPF
         {
             programView.RemoveAllRightLines();
             rightPolyLines = new Dictionary<int, Shape>();
+            //Reset the similarity display
+            UpdateSimilarityScore(Double.NaN);
         }
 
         /// <summary>
@@ -333,6 +312,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 +343,8 @@ namespace SketchAssistantWPF
             }
             programView.SetCanvasState("LeftCanvas", true);
             programView.SetCanvasState("RightCanvas", true);
+
+            LeftLines = lines;
         }
 
         /// <summary>
@@ -421,19 +416,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>
@@ -460,6 +456,25 @@ namespace SketchAssistantWPF
             //programView.SetOptiTrackText("X: ");// + x + "Y: " + y + "Z: " + z);
         }
 
+        /// <summary>
+        /// 
+        /// </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());
+            }
+        }
+
         /*************************/
         /*** HELPING FUNCTIONS ***/
         /*************************/

+ 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);
     }
 }

+ 26 - 27
SketchAssistant/SketchAssistantWPF/MainWindow.xaml

@@ -1,4 +1,4 @@
-<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" 
@@ -6,27 +6,21 @@
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Title="Sketch Assistant" Height="612" Width="914" SizeChanged="Window_SizeChanged">
     <Grid>
         <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 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 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,13 @@
                     </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,6 +226,8 @@
                 <TextBox Name="LastActionBox" Text="none" Background="LightGray"/>
                 <Separator/>
                 <TextBox Name="OptiTrackBox" Text="none" Background="LightGray"/>
+                <Separator/>
+                <TextBox Name="LineSimilarityBox" Text="-" Background="LightGray"/>
             </StatusBar>
         </DockPanel>
     </Grid>

+ 51 - 32
SketchAssistant/SketchAssistantWPF/MainWindow.xaml.cs

@@ -92,7 +92,11 @@ 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>
+        int markerRadius = 10;
 
         /********************************************/
         /*** WINDOW SPECIFIC FUNCTIONS START HERE ***/
@@ -113,7 +117,6 @@ namespace SketchAssistantWPF
         public void RightCanvas_StrokeCollection(object sender, InkCanvasStrokeCollectedEventArgs e)
         {
             strokeCollection.Add(e.Stroke);
-            System.Diagnostics.Debug.WriteLine(strokeCollection.Count);
         }
 
         /// <summary>
@@ -121,7 +124,7 @@ namespace SketchAssistantWPF
         /// </summary>
         private void RedoButton_Click(object sender, RoutedEventArgs e)
         {
-            ProgramPresenter.Redo();
+            if (!IsMousePressed()) ProgramPresenter.Redo();
         }
 
         /// <summary>
@@ -129,7 +132,7 @@ namespace SketchAssistantWPF
         /// </summary>
         private void UndoButton_Click(object sender, RoutedEventArgs e)
         {
-            ProgramPresenter.Undo();
+            if (!IsMousePressed()) ProgramPresenter.Undo();
         }
 
         /// <summary>
@@ -166,8 +169,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>
@@ -177,16 +179,36 @@ namespace SketchAssistantWPF
         {
             if (strokeCollection.Count == 0)
             {
-                ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up_Invalid);
+                ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up_Invalid, strokeCollection);
             }
             else
             {
-                ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up);
+                ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up, strokeCollection);
                 RightCanvas.Strokes.RemoveAt(0);
                 strokeCollection.RemoveAt(0);
-                System.Diagnostics.Debug.WriteLine(strokeCollection.Count);
             }
-            //System.Diagnostics.Debug.WriteLine("ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up);");
+        }
+
+        /// <summary>
+        /// Is called when a stylus is lifted, which has the same effect as releasing the mouse.
+        /// Lifting the finger when using touch also toggles this, therfore this function is sufficient.
+        /// </summary>
+        private void RightCanvas_IsStylusCapturedChanged(object sender, DependencyPropertyChangedEventArgs e)
+        {
+            System.Diagnostics.Debug.WriteLine("Stylus Capture is now: {0}", RightCanvas.IsStylusCaptured);
+            if (!RightCanvas.IsStylusCaptured)
+            {
+                if (strokeCollection.Count == 0)
+                {
+                    ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up_Invalid, strokeCollection);
+                }
+                else
+                {
+                    ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up, strokeCollection);
+                    RightCanvas.Strokes.RemoveAt(0);
+                    strokeCollection.RemoveAt(0);
+                }
+            }
         }
 
         /// <summary>
@@ -195,7 +217,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 +237,17 @@ 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())
+                RightCanvas.EditingMode = InkCanvasEditingMode.Ink;
         }
+
         /*************************/
         /*** PRESENTER -> VIEW ***/
         /*************************/
@@ -389,6 +402,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 +500,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>
@@ -605,7 +624,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 +635,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;
         }

+ 11 - 4
SketchAssistant/SketchAssistantWPF/SketchAssistantWPF.csproj

@@ -1,6 +1,7 @@
 <?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')" />
+<Project ToolsVersion="15.0" 
+  xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="..\packages\ReportGenerator.4.0.15\build\netstandard2.0\ReportGenerator.props" Condition="Exists('..\packages\ReportGenerator.4.0.15\build\netstandard2.0\ReportGenerator.props')" />
   <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -17,6 +18,7 @@
     <Deterministic>true</Deterministic>
     <NuGetPackageImportStamp>
     </NuGetPackageImportStamp>
+    <IsWebBootstrapper>false</IsWebBootstrapper>
     <PublishUrl>publish\</PublishUrl>
     <Install>true</Install>
     <InstallFrom>Disk</InstallFrom>
@@ -29,7 +31,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 +73,9 @@
     <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
     <Prefer32Bit>true</Prefer32Bit>
   </PropertyGroup>
+  <PropertyGroup>
+    <ApplicationIcon>nicubunu-Quill.ico</ApplicationIcon>
+  </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="System.Data" />
@@ -173,11 +177,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>

BIN
SketchAssistant/SketchAssistantWPF/nicubunu-Quill.ico


+ 2 - 2
SketchAssistant/SketchAssistantWPF/packages.config

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="Microsoft.TestPlatform" version="16.0.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>

+ 354 - 20
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,12 +104,161 @@ 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(20);
+            string[] files = Directory.GetFiles(getSketchAssistantDirectory() + @"\whitelisted", "*.svg", SearchOption.AllDirectories);
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("LoadMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("SVGMenuButton")).Click();
+            Thread.Sleep(1000);
+            inputSimulator.Keyboard.TextEntry(getSketchAssistantDirectory() + @"whitelisted\" + filename + ".svg");
+            Thread.Sleep(1000);
+            inputSimulator.Keyboard.KeyPress(VirtualKeyCode.RETURN);
+            Thread.Sleep(1000);
+            //Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DirectInput")]
+        public void DrawLineOnCanvasTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            InputSimulator inputSimulator = new InputSimulator();
+            MouseSimulator mouseSimulator = new MouseSimulator(inputSimulator);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            inputSimulator.Mouse.MoveMouseBy(100, 100);
+            inputSimulator.Mouse.LeftButtonDown();
+            Thread.Sleep(20);
+            inputSimulator.Mouse.MoveMouseBy(100, 100);
+            Thread.Sleep(20);
+            inputSimulator.Mouse.LeftButtonUp();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DirectInput")]
+        public void UndoLineOnCanvasTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            InputSimulator inputSimulator = new InputSimulator();
+            MouseSimulator mouseSimulator = new MouseSimulator(inputSimulator);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            inputSimulator.Mouse.MoveMouseBy(0, 200);
+            inputSimulator.Mouse.LeftButtonDown();
+            Thread.Sleep(20);
+            inputSimulator.Mouse.MoveMouseBy(500, 300);
+            Thread.Sleep(20);
+            inputSimulator.Mouse.LeftButtonUp();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(100);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DirectInput")]
+        public void InvalidLineTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            InputSimulator inputSimulator = new InputSimulator();
+            MouseSimulator mouseSimulator = new MouseSimulator(inputSimulator);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("DrawButton")).Click();
+            Thread.Sleep(20);
+            inputSimulator.Mouse.LeftButtonDown();
+            inputSimulator.Mouse.MoveMouseBy(0, 200);
+            Thread.Sleep(20);
+            inputSimulator.Mouse.MoveMouseBy(500, 300);
+            Thread.Sleep(20);
+            inputSimulator.Mouse.LeftButtonUp();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            inputSimulator.Mouse.MoveMouseBy(-1000, 0);
+            Thread.Sleep(20);
+            inputSimulator.Mouse.LeftButtonDown();
+            inputSimulator.Mouse.MoveMouseBy(1000, 0);
+            inputSimulator.Mouse.LeftButtonUp();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Close();
+        }
+
         [TestMethod]
-        [TestCategory("bla")]
+        [TestCategory("DirectInput")]
+        public void PointsOnCanvasSimilarityTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            InputSimulator inputSimulator = new InputSimulator();
+            MouseSimulator mouseSimulator = new MouseSimulator(inputSimulator);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Assert.AreEqual("-", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LineSimilarityBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            inputSimulator.Mouse.MoveMouseBy(0, 200);
+            inputSimulator.Mouse.LeftButtonDown();
+            Thread.Sleep(20);
+            inputSimulator.Mouse.LeftButtonUp();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Assert.AreEqual("-", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LineSimilarityBox")).Text.ToString());
+            Thread.Sleep(20);
+            inputSimulator.Mouse.LeftButtonDown();
+            Thread.Sleep(20);
+            inputSimulator.Mouse.LeftButtonUp();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Assert.AreEqual("1", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LineSimilarityBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Assert.AreEqual("-", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LineSimilarityBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("RedoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Assert.AreEqual("1", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LineSimilarityBox")).Text.ToString());
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DebugInput")]
         public void CreateCanvasTest()
         {
             Window mainWindow = setupapp();
@@ -118,7 +271,7 @@ namespace WhiteTests
         }
 
         [TestMethod]
-        [TestCategory("bla")]
+        [TestCategory("DebugInput")]
         public void DrawLineTest()
         {
             Window mainWindow = setupapp();
@@ -138,6 +291,7 @@ namespace WhiteTests
         }
 
         [TestMethod]
+        [TestCategory("DebugInput")]
         public void DeleteLineTest()
         {
             Window mainWindow = setupapp();
@@ -169,7 +323,7 @@ namespace WhiteTests
         }
 
         [TestMethod]
-        [TestCategory("bla")]
+        [TestCategory("DebugInput")]
         public void UndoTest()
         {
             Window mainWindow = setupapp();
@@ -193,7 +347,7 @@ namespace WhiteTests
         }
 
         [TestMethod]
-        [TestCategory("bla")]
+        [TestCategory("DebugInput")]
         public void RedoTest()
         {
             Window mainWindow = setupapp();
@@ -221,7 +375,8 @@ namespace WhiteTests
         }
 
         [TestMethod]
-        public void DrawSeveralLines()
+        [TestCategory("DebugInput")]
+        public void DrawSeveralLinesTest()
         {
             Window mainWindow = setupapp();
             Thread.Sleep(20);
@@ -249,7 +404,8 @@ namespace WhiteTests
         }
 
         [TestMethod]
-        public void DeleteSeveralLines()
+        [TestCategory("DebugInput")]
+        public void DeleteSeveralLinesTest()
         {
             Window mainWindow = setupapp();
             Thread.Sleep(20);
@@ -288,7 +444,8 @@ namespace WhiteTests
         }
 
         [TestMethod]
-        public void UndoSeveralLines()
+        [TestCategory("DebugInput")]
+        public void UndoSeveralLinesTest()
         {
             Window mainWindow = setupapp();
             Thread.Sleep(20);
@@ -324,7 +481,8 @@ namespace WhiteTests
         }
 
         [TestMethod]
-        public void RedoSeveralLines()
+        [TestCategory("DebugInput")]
+        public void RedoSeveralLinesTest()
         {
             Window mainWindow = setupapp();
             Thread.Sleep(20);
@@ -368,7 +526,8 @@ namespace WhiteTests
         }
 
         [TestMethod]
-        public void UndoAndRedoTests()
+        [TestCategory("DebugInput")]
+        public void UndoAndRedoTest()
         {
             Window mainWindow = setupapp();
             Thread.Sleep(20);
@@ -426,7 +585,80 @@ namespace WhiteTests
         }
 
         [TestMethod]
-        public void PointDraw()
+        [TestCategory("DebugInput")]
+        public void UndoAndDrawTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugFour")).Click();
+            Thread.Sleep(2000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("UndoButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugFour")).Click();
+            Thread.Sleep(2000);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DebugInput")]
+        public void DeleteSeveralLinesAtOnceTest()
+        {
+            Window mainWindow = setupapp();
+            Thread.Sleep(20);
+            Assert.AreEqual("none", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("CanvasButton")).Click();
+            Thread.Sleep(20);
+            Assert.AreEqual("Last Action: A new canvas was created.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugFour")).Click();
+            Thread.Sleep(2000);
+            Assert.AreEqual("Last Action: Line number 0 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugFour")).Click();
+            Thread.Sleep(2000);
+            Assert.AreEqual("Last Action: Line number 1 was drawn.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Get<Button>(SearchCriteria.ByAutomationId("DeleteButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("EditMenuButton")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugMode")).Click();
+            Thread.Sleep(20);
+            mainWindow.Get<Menu>(SearchCriteria.ByAutomationId("DebugFour")).Click();
+            Thread.Sleep(2000);
+            Assert.AreEqual("Last Action: Several Lines were deleted.", mainWindow.Get<TextBox>(SearchCriteria.ByAutomationId("LastActionBox")).Text.ToString());
+            Thread.Sleep(20);
+            mainWindow.Close();
+        }
+
+        [TestMethod]
+        [TestCategory("DebugInput")]
+        public void PointDrawTest()
         {
             Window mainWindow = setupapp();
             Thread.Sleep(20);
@@ -446,7 +678,8 @@ namespace WhiteTests
         }
 
         [TestMethod]
-        public void NewCanvasAfterDraw()
+        [TestCategory("DebugInput")]
+        public void NewCanvasAfterDrawTest()
         {
             Window mainWindow = setupapp();
             Thread.Sleep(20);
@@ -491,7 +724,6 @@ namespace WhiteTests
     }
 
     [TestClass]
-    [DeploymentItem(@"WhiteTests\test_input_files\")]
     public class FileImporterTests
     {
         /// <summary>
@@ -556,7 +788,7 @@ namespace WhiteTests
         /// <param name="xCoordinates">an array containing the x coordinates of the points that will be created (length divisible by 3)</param>
         /// <param name="yCoordinates">an array containing the y coordinates of the points that will be created (length divisible by 3)</param>
         [DataTestMethod]
-        [TestCategory("bla")]
+        [TestCategory("FileIO")]
         [DataRow(new int[] { 54, 43, 57, 11, 145, 34, 113, 299, 0 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 })]
         [DataRow(new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 })]
         [DataRow(new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 54, 43, 57, 11, 145, 34, 113, 199, 0 })]
@@ -597,7 +829,7 @@ namespace WhiteTests
         /// </summary>
         /// <param name="file">the input file represented as an array of lines</param>
         [DataTestMethod]
-        [TestCategory("bla")]
+        [TestCategory("FileIO")]
         [DataRow(new String[] { })]
         [DataRow(new String[] { "begindrawing", "300x300", "line", "50;50", "100;50", "endline", "enddrawing" })]
         [DataRow(new String[] { "drawing", "300;300", "line", "50;50", "100;50", "endline", "enddrawing" })]
@@ -623,12 +855,13 @@ namespace WhiteTests
                 //try to initialize the left image with an invalid isad drawing
                 Tuple<int, int, List<InternalLine>> values1 = uut.ParseISADInputForTesting(file);
             }
-            catch (FileImporterException)
+            catch (FileImporterException e)
             {
                 //save the occurence of an exception
                 correctExceptionThrown = true;
+                System.Diagnostics.Debug.WriteLine(e.ToString());
             }
-            catch (Exception e)
+            catch (Exception)
             {
                 //don't set success flag
             }
@@ -656,7 +889,7 @@ namespace WhiteTests
                 {
                     uut.ParseSVGInputFile(s, 10000, 10000);
                 }
-                catch (Exception e)
+                catch (Exception)
                 {
                     noExceptionThrown = false;
                 }
@@ -684,13 +917,114 @@ namespace WhiteTests
                 }
                 catch (FileImporterException e)
                 {
+                    System.Diagnostics.Debug.WriteLine(e.ToString());
                     correctExceptionThrown = true;
                 }
-                catch (Exception e)
+                catch (Exception)
                 {
                 }
                 Assert.IsTrue(correctExceptionThrown);
             }
         }
     }
+
+    [TestClass]
+    public class SimilarityCalculationTests
+    {
+        /// <summary>
+        /// The debug data element used to generate random lines.
+        /// </summary>
+        private DebugData DebugData = new DebugData();
+
+        /// <summary>
+        /// Generates random lines and tests how similar they are. 
+        /// To test the similarity score always stays between 0 and 1.
+        /// </summary>
+        [TestMethod]
+        [TestCategory("UnitTest")]
+        public void StaysWithinParameters()
+        {
+            Parallel.For(1, 100,
+                i =>
+                {
+                    InternalLine l0 = DebugData.GetRandomLine(1, (uint)i);
+                    InternalLine l1 = DebugData.GetRandomLine(1, (uint)i);
+                    var sim = GeometryCalculator.CalculateSimilarity(l0, l1);
+                    Assert.IsTrue((sim >= 0));
+                    Assert.IsTrue((sim <= 1));
+                });
+        }
+
+        [TestMethod]
+        [TestCategory("UnitTest")]
+        public void CorrectSimilarity()
+        {
+            Parallel.ForEach(DebugData.GetSimilarityTestData(),
+                tup =>
+                {
+                    InternalLine l0 = tup.Item1;
+                    InternalLine l1 = tup.Item2;
+                    var sim = GeometryCalculator.CalculateSimilarity(l0, l1);
+                    Assert.AreEqual(tup.Item3, sim, 0.00000001);
+                });
+        }
+
+    }
+
+    [TestClass]
+    public class InternalLineUnitTests
+    {
+        /// <summary>
+        /// The debug data element used to generate random lines.
+        /// </summary>
+        private DebugData DebugData = new DebugData();
+
+        [TestMethod]
+        [TestCategory("UnitTest")]
+        public void MakePermanentTest()
+        {
+
+            List<Point> points = new List<Point>();
+            points.AddRange(DebugData.debugPoints4);
+            InternalLine uut = new InternalLine(points);
+            Assert.AreEqual(false, uut.isPoint);
+            uut.MakePermanent(5);
+            Assert.AreEqual(true, uut.isPoint);
+            Assert.AreEqual(5, uut.GetID());
+            Assert.AreEqual(0, uut.GetLength());
+        }
+
+        [DataTestMethod]
+        [TestCategory("UnitTest")]
+        [DataRow(new int[] { 1, 1, 3, 3 }, new int[] { 1, 1, 2, 2, 3, 3 }, false, 2.828427125)]
+        [DataRow(new int[] { 1, 1, 3, 3 }, new int[] { 1, 1, 2, 2, 3, 3 }, true, 2.828427125)]
+        [DataRow(new int[] { 1, 1, 1, 4, 3, 4 }, new int[] { 1, 1, 1, 2, 1, 3, 1, 4, 2, 4, 3, 4 }, false, 5)]
+        [DataRow(new int[] { 1, 1, 1, 4, 3, 4 }, new int[] { 1, 1, 1, 2, 1, 3, 1, 4, 2, 4, 3, 4 }, true, 5)]
+        public void PermanentLineTest(int[] inPoints, int[] outPoints, bool isTemp, double len)
+        {
+            List<Point> inLine = new List<Point>(); List<Point> outLine = new List<Point>();
+            for (int i = 0; i < inPoints.Length; i += 2) inLine.Add(new Point(inPoints[i], inPoints[i + 1]));
+            for (int i = 0; i < outPoints.Length; i += 2) outLine.Add(new Point(outPoints[i], outPoints[i + 1]));
+            InternalLine uut;
+            if (isTemp)
+            {
+                uut = new InternalLine(inLine);
+                var zip = inLine.Zip(uut.GetPoints(), (a, b) => new Tuple<Point, Point>(a, b));
+                foreach (Tuple<Point, Point> tup in zip)
+                {
+                    Assert.AreEqual(tup.Item1, tup.Item2);
+                }
+            }
+            else
+            {
+                uut = new InternalLine(inLine, 0);
+                var zip = outLine.Zip(uut.GetPoints(), (a, b) => new Tuple<Point, Point>(a, b));
+                foreach (Tuple<Point, Point> tup in zip)
+                {
+                    Assert.AreEqual(tup.Item1, tup.Item2);
+                }
+            }
+            Assert.AreEqual(len, uut.GetLength(), 0.000001);
+        }
+    }
 }

+ 4 - 2
SketchAssistant/WhiteTests/WhiteTests.csproj

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="15.0" DefaultTargets="Build" 
   xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <Import Project="..\packages\ReportGenerator.4.0.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="..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.props')" />
   <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
   <PropertyGroup>
@@ -53,9 +53,11 @@
     <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
       <HintPath>..\packages\MSTest.TestFramework.1.4.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
     </Reference>
+    <Reference Include="PresentationFramework" />
     <Reference Include="System" />
     <Reference Include="System.Configuration" />
     <Reference Include="System.Core" />
+    <Reference Include="System.Drawing" />
     <Reference Include="System.Windows" />
     <Reference Include="System.Windows.Forms" />
     <Reference Include="TestStack.White, Version=0.13.0.0, Culture=neutral, PublicKeyToken=2672efbf3e161801, processorArchitecture=MSIL">
@@ -119,7 +121,7 @@
     </PropertyGroup>
     <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.props'))" />
     <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.targets'))" />
-    <Error Condition="!Exists('..\packages\ReportGenerator.4.0.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>
   <Import Project="..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.targets')" />
   <PropertyGroup>

Some files were not shown because too many files changed in this diff