Browse Source

Userstory20 (#17)

* Added Optitrack
* Added TTP file
-cleaned up ui
-optimised startup for systems without optitrack
* calibrated parameters for converting optitrack to pixel
* Fixed errors
* Added color options
* Added check to prevent drawing when moving back through drawing plane
* Added function to calculate when to vibrate pen
m-edlund 5 years ago
parent
commit
dc01cc6b94
28 changed files with 1034 additions and 655 deletions
  1. 15 16
      .appveyor.yml
  2. 0 0
      Neues Textdokument.txt
  3. BIN
      SketchAssistant/NPTrackingTools.dll
  4. 0 450
      SketchAssistant/SketchAssistant.Tests/UnitTest1.cs
  5. 10 0
      SketchAssistant/SketchAssistant.sln
  6. 2 1
      SketchAssistant/SketchAssistantWPF/App.xaml
  7. 18 0
      SketchAssistant/SketchAssistantWPF/CustomCanvas.cs
  8. 1 1
      SketchAssistant/SketchAssistantWPF/DebugData.cs
  9. 32 30
      SketchAssistant/SketchAssistantWPF/FileImporter.cs
  10. 39 0
      SketchAssistant/SketchAssistantWPF/Frame.cs
  11. 5 5
      SketchAssistant/SketchAssistantWPF/GeometryCalculator.cs
  12. 39 0
      SketchAssistant/SketchAssistantWPF/HighPerformanceTimer.cs
  13. 11 10
      SketchAssistant/SketchAssistantWPF/InternalLine.cs
  14. 287 38
      SketchAssistant/SketchAssistantWPF/MVP_Model.cs
  15. 172 74
      SketchAssistant/SketchAssistantWPF/MVP_Presenter.cs
  16. 6 0
      SketchAssistant/SketchAssistantWPF/MVP_View.cs
  17. 44 1
      SketchAssistant/SketchAssistantWPF/MainWindow.xaml
  18. 55 18
      SketchAssistant/SketchAssistantWPF/MainWindow.xaml.cs
  19. BIN
      SketchAssistant/SketchAssistantWPF/NPTrackingTools.dll
  20. 195 0
      SketchAssistant/SketchAssistantWPF/OptiTrackConnector.cs
  21. 39 0
      SketchAssistant/SketchAssistantWPF/SketchAssistantWPF.csproj
  22. 24 0
      SketchAssistant/SketchAssistantWPF/Wristband.cs
  23. BIN
      SketchAssistant/SketchAssistantWPF/optitrack_setup.ttp
  24. 1 0
      SketchAssistant/SketchAssistantWPF/packages.config
  25. 26 8
      SketchAssistant/WhiteTests/UITest.cs
  26. 0 3
      SketchAssistant/WhiteTests/packages.config
  27. BIN
      SketchAssistant/optitrack_setup.ttp
  28. 13 0
      userstory20.md

+ 15 - 16
.appveyor.yml

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

+ 0 - 0
Neues Textdokument.txt


BIN
SketchAssistant/NPTrackingTools.dll


+ 0 - 450
SketchAssistant/SketchAssistant.Tests/UnitTest1.cs

@@ -1,450 +0,0 @@
-using System;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System.Drawing;
-using System.Collections.Generic;
-using SketchAssistant;
-using System.Windows.Forms;
-using System.IO;
-
-namespace Tests
-{
-    [TestClass]
-    public class LineTests
-    {
-        //========================//
-        //= Bresenham Line Tests =//
-        //========================//
-
-        [TestMethod]
-        public void BresenhamLineTest1()
-        {
-            //Test point
-            List<Point> expectedResult = new List<Point>();
-            expectedResult.Add(new Point(1, 2));
-            List<Point> actualResult = SketchAssistant.GeometryCalculator.BresenhamLineAlgorithm(new Point(1, 2), new Point(1, 2));
-            Assert.AreEqual(1, actualResult.Count);
-            for (int i = 0; i < actualResult.Count; i++)
-            {
-                Assert.AreEqual(expectedResult[i], actualResult[i]);
-            }
-        }
-
-        [TestMethod]
-        public void BresenhamLineTest2()
-        {
-            //Test line going from left to right
-            List<Point> expectedResult = new List<Point>();
-            for (int i = 1; i <= 6; i++) { expectedResult.Add(new Point(i, 2)); }
-            List<Point> actualResult = SketchAssistant.GeometryCalculator.BresenhamLineAlgorithm(new Point(1, 2), new Point(6, 2));
-            Assert.AreEqual(expectedResult.Count, actualResult.Count);
-            for (int i = 0; i < actualResult.Count; i++)
-            {
-                Assert.AreEqual(expectedResult[i], actualResult[i]);
-            }
-        }
-
-        [TestMethod]
-        public void BresenhamLineTest3()
-        {
-            //Test line going from right to left
-            List<Point> expectedResult = new List<Point>();
-            for (int i = 6; i >= 1; i--) { expectedResult.Add(new Point(i, 2)); }
-            List<Point> actualResult = SketchAssistant.GeometryCalculator.BresenhamLineAlgorithm(new Point(6, 2), new Point(1, 2));
-            Assert.AreEqual(expectedResult.Count, actualResult.Count);
-            for (int i = 0; i < actualResult.Count; i++)
-            {
-                Assert.AreEqual(expectedResult[i], actualResult[i]);
-            }
-        }
-
-        [TestMethod]
-        public void BresenhamLineTest4()
-        {
-            //Test line going from top to bottom
-            List<Point> expectedResult = new List<Point>();
-            for (int i = 5; i <= 25; i++) { expectedResult.Add(new Point(7, i)); }
-            List<Point> actualResult = SketchAssistant.GeometryCalculator.BresenhamLineAlgorithm(new Point(7, 5), new Point(7, 25));
-            Assert.AreEqual(expectedResult.Count, actualResult.Count);
-            for (int i = 0; i < actualResult.Count; i++)
-            {
-                Assert.AreEqual(expectedResult[i], actualResult[i]);
-            }
-        }
-
-        [TestMethod]
-        public void BresenhamLineTest5()
-        {
-            //Test line going from bottom to top
-            List<Point> expectedResult = new List<Point>();
-            for (int i = 25; i >= 5; i--) { expectedResult.Add(new Point(7, i)); }
-            List<Point> actualResult = SketchAssistant.GeometryCalculator.BresenhamLineAlgorithm(new Point(7, 25), new Point(7, 5));
-            Assert.AreEqual(expectedResult.Count, actualResult.Count);
-            for (int i = 0; i < actualResult.Count; i++)
-            {
-                Assert.AreEqual(expectedResult[i], actualResult[i]);
-            }
-        }
-
-        [TestMethod]
-        public void BresenhamLineTest6()
-        {
-            //Test exactly diagonal line from top left to bottom right
-            List<Point> expectedResult = new List<Point>();
-            for (int i = 5; i <= 25; i++) { expectedResult.Add(new Point(i + 2, i)); }
-            List<Point> actualResult = SketchAssistant.GeometryCalculator.BresenhamLineAlgorithm(new Point(7, 5), new Point(27, 25));
-            Assert.AreEqual(expectedResult.Count, actualResult.Count);
-            for (int i = 0; i < actualResult.Count; i++)
-            {
-                Assert.AreEqual(expectedResult[i], actualResult[i]);
-            }
-        }
-
-        [TestMethod]
-        public void BresenhamLineTest7()
-        {
-            //Test exactly diagonal line from bottom right to top left
-            List<Point> expectedResult = new List<Point>();
-            for (int i = 25; i >= 5; i--) { expectedResult.Add(new Point(i + 2, i)); }
-            List<Point> actualResult = SketchAssistant.GeometryCalculator.BresenhamLineAlgorithm(new Point(27, 25), new Point(7, 5));
-            Assert.AreEqual(expectedResult.Count, actualResult.Count);
-            for (int i = 0; i < actualResult.Count; i++)
-            {
-                Assert.AreEqual(expectedResult[i], actualResult[i]);
-            }
-        }
-
-        //===========================//
-        //= Matrix Population Tests =//
-        //===========================//
-
-        [TestMethod]
-        public void MatrixTest1()
-        {
-            //Populate Matrix for temporary Line
-            List<Point> thePoints = new List<Point>();
-            thePoints.Add(new Point(1, 1));
-            thePoints.Add(new Point(1, 2));
-            bool[,] testBoolMatrix = new bool[5, 5];
-            List<int>[,] testLineMatrix = new List<int>[5, 5];
-            bool[,] resultBoolMatrix = new bool[5, 5];
-            HashSet<int>[,] resultLineMatrix = new HashSet<int>[5, 5];
-            Line testLine = new Line(thePoints);
-            testLine.PopulateMatrixes(resultBoolMatrix, resultLineMatrix);
-            for (int i = 0; i < 5; i++)
-            {
-                for (int j = 0; j < 5; j++)
-                {
-                    Assert.AreEqual(testBoolMatrix[i, j], resultBoolMatrix[i, j]);
-                    Assert.AreEqual(testLineMatrix[i, j], resultLineMatrix[i, j]);
-                }
-            }
-        }
-
-        [TestMethod]
-        public void MatrixTest2()
-        {
-            //Populate Matrix for non-temporary Line
-            List<Point> thePoints = new List<Point>();
-            thePoints.Add(new Point(1, 1));
-            thePoints.Add(new Point(3, 3));
-            bool[,] testBoolMatrix = new bool[5, 5];
-            HashSet<int>[,] testLineMatrix = new HashSet<int>[5, 5];
-            for (int i = 1; i <= 3; i++)
-            {
-                testBoolMatrix[i, i] = true;
-                HashSet<int> temp = new HashSet<int>();
-                temp.Add(5);
-                testLineMatrix[i, i] = temp;
-            }
-            bool[,] resultBoolMatrix = new bool[5, 5];
-            HashSet<int>[,] resultLineMatrix = new HashSet<int>[5, 5];
-            Line testLine = new Line(thePoints, 5);
-            testLine.PopulateMatrixes(resultBoolMatrix, resultLineMatrix);
-            for (int i = 0; i < 5; i++)
-            {
-                for (int j = 0; j < 5; j++)
-                {
-                    Assert.AreEqual(testBoolMatrix[i, j], resultBoolMatrix[i, j]);
-                    if (testLineMatrix[i, j] != null && resultLineMatrix[i, j] != null)
-                    {
-                        for (int k = 0; k < resultLineMatrix[i, j].Count; k++)
-                        {
-                            Assert.AreEqual(true, testLineMatrix[i, j].SetEquals(resultLineMatrix[i, j]));
-                        }
-                    }
-                }
-            }
-        }
-
-        //=========================//
-        //= Line Constructor Test =//
-        //=========================//
-
-        [TestMethod]
-        public void ConstructorTest()
-        {
-            //Create non-temporary Line and check points
-            //reference Points
-            List<Point> comparisonPoints = new List<Point> {new Point(2,2), new Point(3, 1), new Point(4, 1), new Point(5, 1), new Point(6, 2),
-                new Point(7, 3), new Point(8, 4), new Point(9, 5), new Point(10, 6), new Point(11, 5), new Point(11, 4), new Point(11, 3),
-                new Point(10, 2), new Point(9, 1), new Point(8, 2), new Point(7, 3), new Point(6, 4), new Point(5, 5), new Point(4, 5),
-                new Point(3, 5), new Point(2, 5), new Point(1, 4)};
-            //test Points, with intermediate points missing & duplicate points
-            List<Point> testPoints = new List<Point> {new Point(2,2), new Point(3, 1), new Point(5, 1), new Point(5, 1), new Point(5, 1),
-                new Point(8, 4), new Point(10, 6), new Point(11, 5), new Point(11, 3), new Point(9, 1), new Point(9, 1), new Point(9, 1),
-                new Point(5, 5), new Point(2, 5), new Point(2, 5), new Point(1, 4) };
-            Line testLine = new Line(testPoints, 0);
-            List<Point> returnedPoints = testLine.GetPoints();
-            Assert.AreEqual(comparisonPoints.Count, returnedPoints.Count);
-            for (int i = 0; i < returnedPoints.Count; i++)
-            {
-                Assert.AreEqual(comparisonPoints[i], returnedPoints[i]);
-            }
-        }
-    }
-
-    [TestClass]
-    public class ActionHistoryTests
-    {
-
-        private ActionHistory GetActionHistory()
-        {
-            return new ActionHistory();
-        }
-
-        [DataTestMethod]
-        [DataRow(SketchAction.ActionType.Start, 5, -1, "A new canvas was created.")]
-        [DataRow(SketchAction.ActionType.Draw, 5, 5, "Line number 5 was drawn.")]
-        [DataRow(SketchAction.ActionType.Delete, 10, 10, "Line number 10 was deleted.")]
-        public void ScetchActionTest1(SketchAction.ActionType type, int id, int exit, String response)
-        {
-            HashSet<int> actualResult = new HashSet<int>();
-            if (!type.Equals(SketchAction.ActionType.Start)) { actualResult.Add(id); }
-            SketchAction testAction = new SketchAction(type, id);
-            Assert.AreEqual(type, testAction.GetActionType());
-            Assert.AreEqual(true, actualResult.SetEquals(testAction.GetLineIDs()));
-            Assert.AreEqual(response, testAction.GetActionInformation());
-        }
-
-        [DataTestMethod]
-        [DataRow(SketchAction.ActionType.Start, 1, 2, 3, "A new canvas was created.")]
-        [DataRow(SketchAction.ActionType.Draw, 3, 3, 3, "Line number 3 was drawn.")]
-        [DataRow(SketchAction.ActionType.Delete, 20, 30, 40, "Several Lines were deleted.")]
-        public void ScetchActionTest2(SketchAction.ActionType type, int id1, int id2, int id3, String response)
-        {
-            HashSet<int> actualResult = new HashSet<int>();
-            if (!type.Equals(SketchAction.ActionType.Start))
-            {
-                actualResult.Add(id1);
-                actualResult.Add(id2);
-                actualResult.Add(id3);
-            }
-            SketchAction testAction = new SketchAction(type, actualResult);
-            Assert.AreEqual(type, testAction.GetActionType());
-            Assert.AreEqual(true, actualResult.SetEquals(testAction.GetLineIDs()));
-            Assert.AreEqual(response, testAction.GetActionInformation());
-        }
-
-        [DataTestMethod]
-        [DataRow(SketchAction.ActionType.Start, SketchAction.ActionType.Start, true)]
-        [DataRow(SketchAction.ActionType.Draw, SketchAction.ActionType.Delete, false)]
-        public void ActionHistoryTest1(SketchAction.ActionType action1, SketchAction.ActionType action2, bool isEmpty)
-        {
-            ActionHistory testHistory = GetActionHistory();
-            if (!action1.Equals(SketchAction.ActionType.Start)) { testHistory.AddNewAction(new SketchAction(action1, 5)); }
-            if (!action2.Equals(SketchAction.ActionType.Start)) { testHistory.AddNewAction(new SketchAction(action2, 5)); }
-            Assert.AreEqual(isEmpty, testHistory.IsEmpty());
-        }
-
-        [DataTestMethod]
-        [DataRow(SketchAction.ActionType.Draw, "Last Action: Line number 0 was drawn.")]
-        [DataRow(SketchAction.ActionType.Delete, "Last Action: Line number 0 was deleted.")]
-        public void ActionHistoryUndoRedoTest(SketchAction.ActionType actionType, String message)
-        {
-            ActionHistory testHistory = GetActionHistory();
-            SketchAction testAction = new SketchAction(actionType, 0);
-            testHistory.AddNewAction(testAction);
-            Assert.AreEqual(true, testHistory.CanUndo());
-            testHistory.MoveAction(true);
-            Assert.AreEqual(true, testHistory.CanRedo());
-            var lastActionLabel = testHistory.MoveAction(false);
-            Assert.AreEqual(actionType, testHistory.GetCurrentAction().GetActionType());
-            Assert.AreEqual(message, lastActionLabel);
-        }
-    }
-
-    [TestClass]
-    [DeploymentItem(@"SketchAssistant.Tests\test_input_files\")]
-    public class FileImporterTests
-    { 
-
-        /// <summary>
-        /// instance of TestContext to be able to access deployed files
-        /// </summary>
-         private TestContext testContextInstance;
-        /// <summary>
-        ///Gets or sets the test context which provides
-        ///information about and functionality for the current test run.
-        ///</summary>
-        public TestContext TestContext
-        {
-            get
-            {
-                return testContextInstance;
-            }
-            set
-            {
-                testContextInstance = value;
-            }
-        }
-        /*
-        [DataTestMethod]
-        [DataRow(new int[] { 54, 43, 57, 11, 145, 34, 113, 299, 0 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 })]
-        [DataRow(new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 })]
-        [DataRow(new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 54, 43, 57, 11, 145, 34, 113, 199, 0 })]
-        public void ParseISADInputSuccessfulTest(int[] xCoordinates, int[] yCoordinates)
-        {
-            Form1 program = new Form1();
-            FileImporter uut = new SketchAssistant.FileImporter();
-
-            List<String> file = new List<string>();
-            file.Add("drawing");
-            file.Add("300x200");
-            for (int i = 0; i < xCoordinates.Length - 2; i += 3)
-            {
-                file.Add("line");
-                file.Add(xCoordinates[i] + ";" + yCoordinates[i]);
-                file.Add(xCoordinates[i + 1] + ";" + yCoordinates[i + 1]);
-                file.Add(xCoordinates[i + 2] + ";" + yCoordinates[i + 2]);
-                file.Add("endline");
-            }
-            file.Add("enddrawing");
-
-            (int, int, List<Line>) values = uut.ParseISADInputForTesting(file.ToArray());
-            program.CreateCanvasAndSetPictureForTesting(values.Item1, values.Item2, values.Item3);
-
-            Line[] drawing = GetLeftImage(program).ToArray();
-
-            Assert.AreEqual(xCoordinates.Length / 3, drawing.Length);
-            for (int i = 0; i < xCoordinates.Length - 2; i += 3)
-            {
-                Point[] currentLine = drawing[i / 3].GetPoints().ToArray();
-                Assert.AreEqual(3, currentLine.Length);
-                for (int j = 0; j < 3; j++)
-                {
-                    Assert.IsTrue(currentLine[j].X == xCoordinates[i + j] && currentLine[j].Y == yCoordinates[i + j]);
-                }
-            }
-        }
-
-        [DataTestMethod]
-        [DataRow(new String[] {})]
-        [DataRow(new String[] { "begindrawing", "300x300", "line", "50;50", "100;50", "endline", "enddrawing" })]
-        [DataRow(new String[] { "drawing", "300;300", "line", "50;50", "100;50", "endline", "enddrawing" })]
-        [DataRow(new String[] { "drawing", "30.5x300", "line", "50;50", "100;50", "endline", "enddrawing" })]
-        [DataRow(new String[] { "drawing", "line", "50;50", "100;50", "endline", "enddrawing" })]
-        [DataRow(new String[] { "drawing", "300x300", "beginline", "50;50", "100;50", "endline", "enddrawing" })]
-        [DataRow(new String[] { "drawing", "300x300", "line", "500;50", "100;50", "endline", "enddrawing" })]
-        [DataRow(new String[] { "drawing", "300x300", "line", "50x50", "100;50", "endline", "enddrawing" })]
-        [DataRow(new String[] { "drawing", "300x300", "line", "50", "100", "endline", "enddrawing" })]
-        [DataRow(new String[] { "drawing", "300x300", "line", "50;50", "line", "endline", "enddrawing" })]
-        [DataRow(new String[] { "drawing", "300x300", "line", "50;50", "100;50", "stopline", "enddrawing" })]
-        [DataRow(new String[] { "drawing", "300x300", "line", "50;50", "100;50", "enddrawing" })]
-        [DataRow(new String[] { "drawing", "300x300", "line", "50;50", "100;50", "endline", "endrawing" })]
-        [DataRow(new String[] { "drawing", "300x300", "line", "50;50", "100;50", "endline" })]
-        public void ParseISADInputExceptionTest(String[] file)
-        {
-            bool exceptionThrown = false;
-            Form1 program = new Form1();
-            FileImporter uut = new SketchAssistant.FileImporter();
-            //check that left image initially is uninitialized
-            Assert.IsNull(GetLeftImage(program));
-            //initialize left image with a valid isad drawing
-            (int, int, List<Line>) values = uut.ParseISADInputForTesting(new string[] { "drawing", "300x205", "line", "40;40", "140;140", "endline", "enddrawing" });
-            program.CreateCanvasAndSetPictureForTesting(values.Item1, values.Item2, values.Item3);
-            //save left image for later comparison
-            List<Line> oldLeftImage = GetLeftImage(program);
-            try
-            {
-                //try to initialize the left image with an invalid isad drawing
-                (int, int, List<Line>) values1 = uut.ParseISADInputForTesting(file);
-                program.CreateCanvasAndSetPictureForTesting(values1.Item1, values1.Item2, values1.Item3);
-            }
-            catch(FileImporterException)
-            {
-                //save the occurence of an exception
-                exceptionThrown = true;
-            }
-            //check that an exception has been thrown
-            Assert.IsTrue(exceptionThrown);
-            //check that the left image has not been changed by the failed image import
-            Assert.AreEqual(oldLeftImage, GetLeftImage(program));
-        }
-
-        /// <summary>
-        /// local helper method retrieving the left image from a Form1 instance
-        /// </summary>
-        /// <returns>the left image of the given Form1 instance</returns>
-        private List<Line> GetLeftImage(Form1 program)
-        {
-            //cast is save as long as Form1#GetAllVariables() is conform to its contract
-            return (List<Line>) program.GetAllVariables()[4];
-        }
-        */
-
-        /// <summary>
-        /// parses all whitelisted files and ensures no exceptions are thrown (parsing abortion, e.g. due to corrupted input files, are realized by throwing a FileImporterException)
-        /// </summary>
-        [TestMethod]
-        public void parseSVGInputNoErrorForWhitelistedFilesTest()
-        {
-            FileImporter uut = new FileImporter();
-
-            string[] files = Directory.GetFiles(TestContext.DeploymentDirectory + @"\test_input_files\whitelisted", "*.svg", SearchOption.AllDirectories);
-            Assert.IsTrue(files.Length > 0);
-
-            foreach (string s in files) //parse each of the whitelisted files
-            {
-                bool noExceptionThrown = true;
-                try
-                {
-                    uut.ParseSVGInputFile(s, 10000, 10000);
-                }
-                catch (Exception e)
-                {
-                    noExceptionThrown = false;
-                }
-                Assert.IsTrue(noExceptionThrown);
-            }
-        }
-
-        /// <summary>
-        /// parses all blacklisted files and ensures an instance of FileIporterException is thrown for each file, but no other exceptions occur
-        /// </summary>
-        [TestMethod]
-        public void parseSVGInputNoErrorForBlacklistedFilesTest()
-        {
-            FileImporter uut = new FileImporter();
-
-            string[] files = Directory.GetFiles(TestContext.DeploymentDirectory + @"\test_input_files\blacklisted", "*.svg", SearchOption.AllDirectories);
-            Assert.IsTrue(files.Length > 0);
-            foreach (string s in files) //parse each of the blacklisted files
-            {
-                bool correctExceptionThrown = false;
-                try
-                {
-                    uut.ParseSVGInputFile(s, 10000, 10000);
-                }
-                catch(FileImporterException e)
-                {
-                    correctExceptionThrown = true;
-                }
-                catch(Exception e)
-                {
-                }
-                Assert.IsTrue(correctExceptionThrown);
-            }
-        }
-    }
-
-}

+ 10 - 0
SketchAssistant/SketchAssistant.sln

@@ -15,17 +15,27 @@ EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
+		Debug|x86 = Debug|x86
 		Release|Any CPU = Release|Any CPU
+		Release|x86 = Release|x86
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
 		{EE53AE79-2AA0-4F43-9638-1789B189D5C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{EE53AE79-2AA0-4F43-9638-1789B189D5C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{EE53AE79-2AA0-4F43-9638-1789B189D5C3}.Debug|x86.ActiveCfg = Debug|x86
+		{EE53AE79-2AA0-4F43-9638-1789B189D5C3}.Debug|x86.Build.0 = Debug|x86
 		{EE53AE79-2AA0-4F43-9638-1789B189D5C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{EE53AE79-2AA0-4F43-9638-1789B189D5C3}.Release|Any CPU.Build.0 = Release|Any CPU
+		{EE53AE79-2AA0-4F43-9638-1789B189D5C3}.Release|x86.ActiveCfg = Release|x86
+		{EE53AE79-2AA0-4F43-9638-1789B189D5C3}.Release|x86.Build.0 = Release|x86
 		{EB09C624-91F2-465F-825B-559BF7A7D5CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{EB09C624-91F2-465F-825B-559BF7A7D5CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{EB09C624-91F2-465F-825B-559BF7A7D5CB}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{EB09C624-91F2-465F-825B-559BF7A7D5CB}.Debug|x86.Build.0 = Debug|Any CPU
 		{EB09C624-91F2-465F-825B-559BF7A7D5CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{EB09C624-91F2-465F-825B-559BF7A7D5CB}.Release|Any CPU.Build.0 = Release|Any CPU
+		{EB09C624-91F2-465F-825B-559BF7A7D5CB}.Release|x86.ActiveCfg = Release|Any CPU
+		{EB09C624-91F2-465F-825B-559BF7A7D5CB}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

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

+ 18 - 0
SketchAssistant/SketchAssistantWPF/CustomCanvas.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Automation.Peers;
+using System.Windows.Controls;
+
+namespace SketchAssistantWPF
+{
+    public class CustomCanvas : Canvas
+    {
+        protected override AutomationPeer OnCreateAutomationPeer()
+        {
+            return new FrameworkElementAutomationPeer(this);
+        }
+    }
+}

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


+ 32 - 30
SketchAssistant/SketchAssistantWPF/FileImporter.cs

@@ -26,7 +26,7 @@ namespace SketchAssistantWPF
         /// <summary>
         /// array containing all characters interpreted as whitespaces which seperate words/tokens in the input file
         /// </summary>
-        readonly char[] whitespaces = new char[] { ' ' , ',' };
+        readonly char[] whitespaces = new char[] { ' ', ',' };
 
         /// <summary>
         /// number of points to create along the outline of an ellipse, divisible by 4
@@ -69,7 +69,7 @@ namespace SketchAssistantWPF
             }
             Tuple<int, int> dimensions = ParseISADHeader(allLines);
             List<InternalLine> picture = ParseISADBody(allLines, dimensions.Item1, dimensions.Item2);
-            
+
             return new Tuple<int, int, List<InternalLine>>(dimensions.Item1, dimensions.Item2, picture);
         }
 
@@ -197,13 +197,13 @@ namespace SketchAssistantWPF
             i++;
             int width; //width of the resulting picture in pixels
             int height; //height of the resulting picture in pixels
-            if(windowWidth != 0 && windowHeight != 0)
+            if (windowWidth != 0 && windowHeight != 0)
             {
                 if (windowWidth / windowHeight > sizedef.Item1 / sizedef.Item2) //height dominant, width has to be smaller than drawing window to preserve xy-scale
                 {
                     scale = windowHeight / sizedef.Item2;
                     height = (int)Math.Round(windowHeight);
-                    width = (int) Math.Round(scale * sizedef.Item1);
+                    width = (int)Math.Round(scale * sizedef.Item1);
                 }
                 else //width dominant, height has to be smaller than drawing window to preserve xy-scale
                 {
@@ -218,7 +218,7 @@ namespace SketchAssistantWPF
                 width = sizedef.Item1;
                 height = sizedef.Item2;
             }
-            for (int j=0; j < allLines.Length; j++)
+            for (int j = 0; j < allLines.Length; j++)
             {
                 allLines[j] = allLines[j].Trim(whitespaces);
             }
@@ -238,9 +238,9 @@ namespace SketchAssistantWPF
                 i++;
             }
             String[] currentLine = allLines[i].Split(' ');
-            int width= -1;
-            int height= -1;
-            for(int j= 0; j < currentLine.Length; j++)
+            int width = -1;
+            int height = -1;
+            for (int j = 0; j < currentLine.Length; j++)
             {
                 if (currentLine[j].StartsWith("width"))
                 {
@@ -251,9 +251,9 @@ namespace SketchAssistantWPF
                     height = Convert.ToInt32(ParseSingleSVGAttribute(currentLine[j]));
                 }
             }
-            if(width == -1)
+            if (width == -1)
             {
-                throw new FileImporterException("missing width definition in SVG header", "the header should contain the \"width=...\" attribute", i+1);
+                throw new FileImporterException("missing width definition in SVG header", "the header should contain the \"width=...\" attribute", i + 1);
             }
             if (height == -1)
             {
@@ -301,7 +301,7 @@ namespace SketchAssistantWPF
         /// <returns>the parsed element as a Line object, or null if the element is not supported</returns>
         private List<InternalLine> ParseSingleLineSVGElement(string[] currentElement)
         {
-            List<Point> points= null;
+            List<Point> points = null;
             List<InternalLine> element = null;
             switch (currentElement[0])
             {
@@ -350,7 +350,7 @@ namespace SketchAssistantWPF
             double h = 0;
             double rx = 0;
             double ry = 0;
-            for(int j= 0; j < currentElement.Length; j++)
+            for (int j = 0; j < currentElement.Length; j++)
             {
                 if (currentElement[j].StartsWith("x="))
                 {
@@ -540,9 +540,9 @@ namespace SketchAssistantWPF
                 }
             }
             List<Point> polygon = new List<Point>();
-            for (int k = 0; k < points.Length - 1; k+=2)
+            for (int k = 0; k < points.Length - 1; k += 2)
             {
-                polygon.Add(ScaleAndCreatePoint(Convert.ToDouble(points[k], CultureInfo.InvariantCulture), Convert.ToDouble(points[k+1], CultureInfo.InvariantCulture)));
+                polygon.Add(ScaleAndCreatePoint(Convert.ToDouble(points[k], CultureInfo.InvariantCulture), Convert.ToDouble(points[k + 1], CultureInfo.InvariantCulture)));
             }
             polygon.Add(ScaleAndCreatePoint(Convert.ToDouble(points[0], CultureInfo.InvariantCulture), Convert.ToDouble(points[1], CultureInfo.InvariantCulture))); //close polygon
             return polygon;
@@ -573,12 +573,12 @@ namespace SketchAssistantWPF
             NormalizePathDeclaration(pathElements); //expand path data to always explicitly have the command descriptor in front of the appropriate number of arguments and to seperate command descriptors, coordinates and other tokens always into seperate list elements (equivalent to seperation with spaces in the input file, but svg allows also for comma as a seperator, and for omitting seperators where possible without losing information (refer to svg grammer) to reduce file size
             List<InternalLine> element = new List<InternalLine>();
             List<Point> currentLine = new List<Point>();
-            double lastBezierControlPointX= 0;
-            double lastBezierControlPointY= 0;
+            double lastBezierControlPointX = 0;
+            double lastBezierControlPointY = 0;
             double lastPositionX;
             double lastPositionY;
-            double initialPositionX= -1;
-            double initialPositionY= -1;
+            double initialPositionX = -1;
+            double initialPositionY = -1;
             bool newSubpath = true;
             Tuple<List<Point>, double, double> valuesArc; //list of points, new values for: lastPositionX, lastPositionY
             Tuple<List<Point>, double, double, double, double> valuesBezierCurve; //list of points, new values for: lastPositionX, lastPositionY, lastBezierControlPointX, lastBezierControlPointY
@@ -588,7 +588,8 @@ namespace SketchAssistantWPF
             lastPositionX = valuesSinglePoint.Item2;
             lastPositionY = valuesSinglePoint.Item3;
             String currentToken;
-            while (!(pathElements.Count == 0)){
+            while (!(pathElements.Count == 0))
+            {
                 if (newSubpath)
                 {
                     initialPositionX = lastPositionX; //update buffers for coordinates of first point of active subpath
@@ -773,7 +774,7 @@ namespace SketchAssistantWPF
         {
             Char lastCommand = 'M';
             int argumentCounter = 0;
-            for( int j= 0; j < pathElements.Count; j++)
+            for (int j = 0; j < pathElements.Count; j++)
             {
                 String currentElement = pathElements.ElementAt(j);
                 if (currentElement.Length != 1)
@@ -788,8 +789,9 @@ namespace SketchAssistantWPF
                     }
                     else if ((currentElement.First() >= '0' && currentElement.First() <= '9') || currentElement.First() == '-' || currentElement.First() == '+' || currentElement.First() != 'e') //seperate a single coordinate / number
                     {
-                        bool repeatCommandDescriptor = false; 
-                        switch (lastCommand){ //check for reaching of next command with omitted command descriptor
+                        bool repeatCommandDescriptor = false;
+                        switch (lastCommand)
+                        { //check for reaching of next command with omitted command descriptor
                             case 'M':
                                 if (argumentCounter >= 2) repeatCommandDescriptor = true;
                                 break;
@@ -866,7 +868,7 @@ namespace SketchAssistantWPF
                                 break;
                             }
                         }
-                        argumentCounter++; 
+                        argumentCounter++;
                     }
                     else //parse non-space seperators and skip other unsupported characters (the only other valid ones per svg standard would be weird tokens looking like format descriptors (e.g. '#xC'), these are unsopported and will likely cause an error or other inconsitencies during parsing)
                     {
@@ -888,7 +890,7 @@ namespace SketchAssistantWPF
                         lastCommand = currentElement.First();
                         argumentCounter = 0;
                     }
-                    else if(!(currentElement.First() >= '0' && currentElement.First() <= '9')) //not a number
+                    else if (!(currentElement.First() >= '0' && currentElement.First() <= '9')) //not a number
                     {
                         pathElements.RemoveAt(j); //remove element
                         j--; //decrement index pointer so next element will not be skipped (indices of all folowing elements just decreased by 1)
@@ -1415,14 +1417,14 @@ namespace SketchAssistantWPF
             {
                 xPlusFlag = true; //left point lies right of line
             }
-            if(sweepFlag != largeArcFlag) //need "right" center point, not "left" one (refer to svg specification, sweepFlag means going around the circle in "clockwise" direction, largeArcFlag means tracing the larger of the two possible arcs in the selected direction) 
+            if (sweepFlag != largeArcFlag) //need "right" center point, not "left" one (refer to svg specification, sweepFlag means going around the circle in "clockwise" direction, largeArcFlag means tracing the larger of the two possible arcs in the selected direction) 
             {
                 xPlusFlag = !xPlusFlag;
                 yPlusFlag = !yPlusFlag;
             }
             double xC; // coordinates of center point of circle
             double yC;
-            if(xPlusFlag) xC = x3 + Math.Sqrt(radsq - ((q / 2) * (q / 2))) * ((nextPositionYRelative) / q); //x3 + Math.Sqrt(radsq - ((q / 2) * (q / 2))) * ((y1 - y2) / q);
+            if (xPlusFlag) xC = x3 + Math.Sqrt(radsq - ((q / 2) * (q / 2))) * ((nextPositionYRelative) / q); //x3 + Math.Sqrt(radsq - ((q / 2) * (q / 2))) * ((y1 - y2) / q);
             else xC = x3 - Math.Sqrt(radsq - ((q / 2) * (q / 2))) * ((nextPositionYRelative) / q);
             if (yPlusFlag) yC = y3 + Math.Sqrt(radsq - ((q / 2) * (q / 2))) * ((nextPositionXRelative) / q); //y3 + Math.Sqrt(radsq - ((q / 2) * (q / 2))) * ((x2-x1) / q);
             else yC = y3 - Math.Sqrt(radsq - ((q / 2) * (q / 2))) * ((nextPositionXRelative) / q);
@@ -1430,7 +1432,7 @@ namespace SketchAssistantWPF
             for (int j = 0; j < values.Item1.Length; j++)
             {
                 values.Item1[j] = values.Item1[j] + xC; //correct center point coordinate bias
-                values.Item2[j] = values.Item2[j] + yC; 
+                values.Item2[j] = values.Item2[j] + yC;
             }
             return values;
         }
@@ -1458,13 +1460,13 @@ namespace SketchAssistantWPF
             }
             else // get smaller angleDifference
             {
-                if(angleDifference > Math.PI) angleDifference = ((double)2 * Math.PI) - angleDifference;  // was larger angleDifference
+                if (angleDifference > Math.PI) angleDifference = ((double)2 * Math.PI) - angleDifference;  // was larger angleDifference
             }
-            int numberOfPoints = (int) Math.Ceiling(angleDifference / angle);  //compute number of points to sample
+            int numberOfPoints = (int)Math.Ceiling(angleDifference / angle);  //compute number of points to sample
             double[] xValues = new double[numberOfPoints];
             double[] yValues = new double[numberOfPoints];
             double phiCurrent = phiStart;
-            for (int j = 0; j < numberOfPoints-1; j++) //compute intermediate points
+            for (int j = 0; j < numberOfPoints - 1; j++) //compute intermediate points
             {
                 if (clockwise) phiCurrent -= angle; //get new angle
                 else phiCurrent += angle;

+ 39 - 0
SketchAssistant/SketchAssistantWPF/Frame.cs

@@ -0,0 +1,39 @@
+namespace OptiTrack
+{
+    public class Frame
+    {
+        public Trackable[] Trackables;
+        public Marker[] Markers;
+
+        public Frame(int markerCount, int trackableCount)
+        {
+            Trackables = new Trackable[trackableCount];
+            Markers = new Marker[markerCount];
+        }
+    }
+
+    public class Trackable
+    {
+        public int Id;
+        public float X, Y, Z, Qx, Qy, Qz, Qw, Pitch, Roll, Yaw;
+        public bool IsTracked;
+
+        internal bool IsAlmostSameCoordinates(Trackable t)
+        {
+            return X == t.X && Y == t.Y && Z == t.Z;
+        }
+    }
+
+    public class Marker
+    {
+        public float X, Y, Z;
+        public Trackable BoundToTrackable;
+
+        public Marker(float x, float y, float z)
+        {
+            X = x;
+            Y = y;
+            Z = z;
+        }
+    }
+}

+ 5 - 5
SketchAssistant/SketchAssistantWPF/GeometryCalculator.cs

@@ -28,7 +28,7 @@ namespace SketchAssistantWPF
             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}", 
+            System.Diagnostics.Debug.WriteLine("Results: CosSim: {0}, LenSim: {1}, AvDist {2}, DistSim: {3}. Total: {4}",
                 CosSim, LenSim, AvDist, DistSim, output);
             return output;
         }
@@ -80,9 +80,9 @@ namespace SketchAssistantWPF
                 //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;}
+                if (points0.Count > points1.Count) { shortL = points1; }
                 double dif = (longL.Count - 1) / (shortL.Count - 1);
-                if(dif > 1)
+                if (dif > 1)
                 {
                     //The longer list is significantly longer
                     //Each element in the shorter list is compared to multiple 
@@ -135,7 +135,7 @@ namespace SketchAssistantWPF
             if (len1 > len0) shorter = len0;
             else shorter = len1;
             if (dif >= shorter) return 0;
-            return (shorter - dif )/shorter;
+            return (shorter - dif) / shorter;
         }
 
         /// <summary>
@@ -148,7 +148,7 @@ namespace SketchAssistantWPF
         {
             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 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));

+ 39 - 0
SketchAssistant/SketchAssistantWPF/HighPerformanceTimer.cs

@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OptiTrack
+{
+    public class HighPerformanceTimer
+    {
+        [DllImport("Kernel32.dll")]
+        private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);
+
+        [DllImport("Kernel32.dll")]
+        private static extern bool QueryPerformanceFrequency(out long lpFrequency);
+
+        private long start;
+        private long freq;
+
+        public HighPerformanceTimer()
+        {
+            start = 0;
+            QueryPerformanceFrequency(out freq);
+        }
+
+        public void Start()
+        {
+            QueryPerformanceCounter(out start);
+        }
+
+        public double Stop()
+        {
+            long stop;
+            QueryPerformanceCounter(out stop);
+            return (double)(stop - start) / (double)freq;
+        }
+    }
+}

+ 11 - 10
SketchAssistant/SketchAssistantWPF/InternalLine.cs

@@ -102,14 +102,14 @@ namespace SketchAssistantWPF
         /// <returns>The length of the line.</returns>
         public double GetLength()
         {
-            if(length < 0)
+            if (length < 0)
             {
                 length = 0;
-                for(int i = 0; i < linePoints.Count - 1; i++)
+                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));
+                    length += Math.Sqrt(Math.Pow((a.X - b.X), 2) + Math.Pow((a.Y - b.Y), 2));
                 }
             }
             return length;
@@ -129,12 +129,12 @@ namespace SketchAssistantWPF
                     if (currPoint.X >= 0 && currPoint.Y >= 0 &&
                         currPoint.X < boolMatrix.GetLength(0) && currPoint.Y < boolMatrix.GetLength(1))
                     {
-                        boolMatrix[(int) currPoint.X, (int) currPoint.Y] = true;
-                        if (listMatrix[(int) currPoint.X, (int) currPoint.Y] == null)
+                        boolMatrix[(int)currPoint.X, (int)currPoint.Y] = true;
+                        if (listMatrix[(int)currPoint.X, (int)currPoint.Y] == null)
                         {
-                            listMatrix[(int) currPoint.X, (int) currPoint.Y] = new HashSet<int>();
+                            listMatrix[(int)currPoint.X, (int)currPoint.Y] = new HashSet<int>();
                         }
-                        listMatrix[(int) currPoint.X, (int) currPoint.Y].Add(identifier);
+                        listMatrix[(int)currPoint.X, (int)currPoint.Y].Add(identifier);
                     }
                 }
             }
@@ -149,16 +149,17 @@ namespace SketchAssistantWPF
             {
                 //check if its a point
                 var localIsPoint = linePoints.All(o => o.X == linePoints.First().X && o.Y == linePoints.First().Y);
-                if (!localIsPoint) {
+                if (!localIsPoint)
+                {
                     List<Point> newList = new List<Point>();
                     List<Point> tempList = new List<Point>();
                     //Since Point is non-nullable, we must ensure the nullPoints, 
                     //which we remove can not possibly be points of the original given line.
-                    int nullValue = (int) linePoints[0].X + 1;
+                    int nullValue = (int)linePoints[0].X + 1;
                     //Fill the gaps between points
                     for (int i = 0; i < linePoints.Count - 1; i++)
                     {
-                        nullValue += (int) linePoints[i + 1].X;
+                        nullValue += (int)linePoints[i + 1].X;
                         List<Point> partialList = GeometryCalculator.BresenhamLineAlgorithm(linePoints[i], linePoints[i + 1]);
                         tempList.AddRange(partialList);
                     }

+ 287 - 38
SketchAssistant/SketchAssistantWPF/MVP_Model.cs

@@ -7,6 +7,9 @@ using System.Threading.Tasks;
 using System.Windows.Controls;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
+using OptiTrack;
+using System.Runtime.InteropServices;
+using System.IO;
 
 namespace SketchAssistantWPF
 {
@@ -21,19 +24,28 @@ namespace SketchAssistantWPF
         /// </summary>
         ActionHistory historyOfActions;
         /// <summary>
-        /// The assistant responsible for the redraw mode
+        /// The connector class used to get frames from the Optitrack system.
         /// </summary>
-        //RedrawAssistant redrawAss;
+        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>
+        public bool optiTrackInUse { get; private set; }
         /// <summary>
         /// Size of deletion area
         /// </summary>
@@ -51,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;
@@ -74,40 +98,93 @@ 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.
         /// </summary>
-        public bool canvasActive {get; set;}
+        public bool canvasActive { get; set; }
         /// <summary>
         /// Indicates if there is a graphic loaded in the left canvas.
         /// </summary>
         public bool graphicLoaded { get; set; }
         /// <summary>
+        /// Whether or not an optitrack system is avaiable.
+        /// </summary>
+        public bool optitrackAvailable { get; private set; }
+        /// <summary>
+        /// 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 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 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>
+        /// keeps track of whether last tick the trackable was inside drawing zone or not.
+        /// </summary>
+        private bool optiTrackInsideDrawingZone = false;
+        /// <summary>
+        /// object of class wristband used for controlling the vibrotactile wristband
+        /// </summary>
+        private Wristband wristband;
+        /// <summary>
+        /// Is set to true when the trackable has passed to the backside of the drawing surface, 
+        /// invalidating all inputs on its way back.
+        /// </summary>
+        bool OptiMovingBack = false;
+        /// <summary>
+        /// The Layer in which the optitrack system was. 0 is drawing layer, -1 is in front, 1 is behind
+        /// </summary>
+        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();
+            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);
+                }
+            }
         }
 
         /**************************/
@@ -115,6 +192,31 @@ namespace SketchAssistantWPF
         /**************************/
 
 
+        /// <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)
+        {
+            if (Math.Abs(optiTrackZ) > WARNING_ZONE_BOUNDARY * 2) return false;
+            return true;
+        }
+
+        /// <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.
         /// </summary>
@@ -131,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>
@@ -155,7 +274,7 @@ namespace SketchAssistantWPF
         /// </summary>
         private void UpdateUI()
         {
-            programPresenter.UpdateUIState(inDrawingMode, historyOfActions.CanUndo(), historyOfActions.CanRedo(), canvasActive, graphicLoaded);
+            programPresenter.UpdateUIState(inDrawingMode, historyOfActions.CanUndo(), historyOfActions.CanRedo(), canvasActive, graphicLoaded, optitrackAvailable, optiTrackInUse);
         }
 
         /// <summary>
@@ -182,6 +301,60 @@ namespace SketchAssistantWPF
             return returnSet;
         }
 
+        /// <summary>
+        /// 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>
+        /// Updates the Optitrack coordiantes, aswell as the marker for optitrack.
+        /// </summary>
+        /// <param name="p">The new cursor position</param>
+        private void SetCurrentFingerPosition(Point p)
+        {
+            Point correctedPoint = ConvertTo96thsOfInch(p);
+
+
+            if (optiTrackZ < -2.2 * WARNING_ZONE_BOUNDARY && OptiLayer > -1)
+            {
+                OptiLayer = -1; OptiMovingBack = false;
+                programPresenter.SetOverlayColor("optipoint", Brushes.Yellow);
+            }
+            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);
+        }
+
         /********************************************/
         /*** FUNCTIONS TO INTERACT WITH PRESENTER ***/
         /********************************************/
@@ -189,12 +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; }
+            if (RightCanvas.Height >= 0 && RightCanvas.Width >= 0) { rightImageSize = RightCanvas; }
             RepopulateDeletionMatrixes();
         }
 
@@ -203,6 +374,8 @@ namespace SketchAssistantWPF
         /// </summary>
         public void ResetRightImage()
         {
+            if(currentLine.Count > 0)
+                FinishCurrentLine(true);
             rightLineList.Clear();
             programPresenter.PassLastActionTaken(historyOfActions.Reset());
             programPresenter.ClearRightLines();
@@ -216,7 +389,6 @@ 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;
@@ -286,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();
             }
@@ -299,10 +470,33 @@ 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();
         }
-        
+
+        /// <summary>
+        /// The function called by the Presenter to set a variable which describes if OptiTrack is in use
+        /// </summary>
+        /// <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.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);
+            }
+        }
+
         /// <summary>
         /// Updates the current cursor position of the model.
         /// </summary>
@@ -310,21 +504,27 @@ namespace SketchAssistantWPF
         public void SetCurrentCursorPosition(Point p)
         {
             currentCursorPosition = p;
-            //Temporary position of the optipoint change, change this when merging with optitrack branch
-            programPresenter.MoveOptiPoint(currentCursorPosition);
             mouseDown = programPresenter.IsMousePressed();
         }
 
         /// <summary>
         /// Start a new Line, when the Mouse is pressed down.
         /// </summary>
-        public void MouseDown()
+        public void StartNewLine()
         {
             mouseDown = true;
-            if (inDrawingMode && mouseDown)
+            if (inDrawingMode)
             {
-                currentLine.Clear();
-                currentLine.Add(currentCursorPosition);
+                if(optiTrackInUse)
+                {
+                    currentLine.Clear();
+                    currentLine.Add(currentOptiCursorPosition);
+                }
+                else if (programPresenter.IsMousePressed())
+                {
+                    currentLine.Clear();
+                    currentLine.Add(currentCursorPosition);
+                }
             }
         }
 
@@ -332,8 +532,9 @@ namespace SketchAssistantWPF
         /// Finish the current Line, when the pressed Mouse is released.
         /// </summary>
         /// <param name="valid">Whether the up event is valid or not</param>
-        public void MouseUp(bool valid)
+        public void FinishCurrentLine(bool valid)
         {
+
             mouseDown = false;
             if (valid)
             {
@@ -343,8 +544,10 @@ namespace SketchAssistantWPF
                     rightLineList.Add(new Tuple<bool, InternalLine>(true, newLine));
                     newLine.PopulateMatrixes(isFilledMatrix, linesMatrix);
                     programPresenter.PassLastActionTaken(historyOfActions.AddNewAction(new SketchAction(SketchAction.ActionType.Draw, newLine.GetID())));
+                    //TODO: For the person implementing overlay: Add check if overlay needs to be added
                     programPresenter.UpdateRightLines(rightLineList);
                     currentLine.Clear();
+                    programPresenter.UpdateCurrentLine(currentLine);
                 }
             }
             else
@@ -359,7 +562,7 @@ namespace SketchAssistantWPF
         /// Overload that is used to pass a list of points to be used when one is available.
         /// </summary>
         /// <param name="p">The list of points</param>
-        public void MouseUp(List<Point> p)
+        public void FinishCurrentLine(List<Point> p)
         {
             mouseDown = false;
             if (inDrawingMode && currentLine.Count > 0)
@@ -381,17 +584,64 @@ namespace SketchAssistantWPF
         {
             if (cursorPositions.Count > 0) { previousCursorPosition = cursorPositions.Dequeue(); }
             else { previousCursorPosition = currentCursorPosition; }
-            cursorPositions.Enqueue(currentCursorPosition);
-            //Drawing
-            if (inDrawingMode && programPresenter.IsMousePressed())
+
+            if(optitrackAvailable)
+                SetCurrentFingerPosition(new Point(optiTrackX, optiTrackY));
+            
+
+            if (optiTrackInUse && inDrawingMode && !OptiMovingBack) // optitrack is being used
             {
-                currentLine.Add(currentCursorPosition);
-                //programPresenter.UpdateCurrentLine(currentLine);
+                //outside of drawing zone
+                if (!CheckInsideDrawingZone(optiTrackZ))
+                {
+                    //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();
+                    }
+                    else currentLine.Add(currentOptiCursorPosition);
+                    programPresenter.UpdateCurrentLine(currentLine);
+                    if (optiTrackZ > WARNING_ZONE_BOUNDARY)
+                    {
+                        wristband.PushForward();
+                    }
+                    else if (optiTrackZ < -1 * WARNING_ZONE_BOUNDARY)
+                    {
+                        wristband.PushBackward();
+                    }
+                }
             }
-            //Deleting
-            if (!inDrawingMode && programPresenter.IsMousePressed())
+            else if( !optiTrackInUse && inDrawingMode)
             {
-                List<Point> uncheckedPoints = GeometryCalculator.BresenhamLineAlgorithm(previousCursorPosition, currentCursorPosition);
+                //drawing without optitrack
+                cursorPositions.Enqueue(currentCursorPosition);
+                if (inDrawingMode && programPresenter.IsMousePressed())
+                {
+                    currentLine.Add(currentCursorPosition);
+                    //programPresenter.UpdateCurrentLine(currentLine);
+                }
+
+            }
+
+            //Deletion mode for optitrack and regular use
+            if (!inDrawingMode)
+            {
+                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)
                 {
                     HashSet<int> linesToDelete = CheckDeletionMatrixesAroundPoint(currPoint, deletionRadius);
@@ -403,7 +653,6 @@ 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);
                     }
                 }

+ 172 - 74
SketchAssistant/SketchAssistantWPF/MVP_Presenter.cs

@@ -5,10 +5,11 @@ using System.Text;
 using System.Threading.Tasks;
 using System.Windows;
 using System.Windows.Controls;
-using System.Windows.Ink;
 using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Shapes;
+using OptiTrack;
+using System.Windows.Ink;
 
 namespace SketchAssistantWPF
 {
@@ -26,18 +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);
-
-        List<double> ImageSimilarity = new List<double>();
-
-        List<InternalLine> LeftLines = new List<InternalLine>();
+        /// <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 ***/
@@ -65,7 +70,6 @@ namespace SketchAssistantWPF
         {
             programView = form;
             programModel = new MVP_Model(this);
-            //Initialize Class Variables
             fileImporter = new FileImporter();
         }
 
@@ -80,10 +84,9 @@ 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);
+            canvasSizeLeft.ChangeDimension(leftPBS.Item1, leftPBS.Item2);
+            canvasSizeRight.ChangeDimension(rightPBS.Item1, rightPBS.Item2);
+            programModel.ResizeEvent(canvasSizeRight);
         }
 
         /// <summary>
@@ -108,6 +111,7 @@ namespace SketchAssistantWPF
                         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);
@@ -137,6 +141,35 @@ 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>
+        public void ChangeOptiTrack(bool usingOptiTrack)
+        {
+            if (programModel.optitrackAvailable)
+            {
+                programModel.SetOptiTrack(usingOptiTrack);
+            }
+        }
+
         /// <summary>
         /// Pass-trough function to undo an action.
         /// </summary>
@@ -173,7 +206,7 @@ namespace SketchAssistantWPF
             }
             if (okToContinue)
             {
-                programModel.ResizeEvent(CanvasSizeLeft, CanvasSizeRight);
+                programModel.ResizeEvent(canvasSizeRight);
                 programModel.ResetRightImage();
                 programModel.CanvasActivated();
                 programModel.ChangeState(true);
@@ -186,7 +219,7 @@ namespace SketchAssistantWPF
         /// Pass-trough when the mouse is moved.
         /// </summary>
         /// <param name="mouseAction">The action which is sent by the View.</param>
-        /// <param name="e">The Mouse event arguments.</param>
+        /// <param name="position">The position of the mouse.</param>
         public void MouseEvent(MouseAction mouseAction, Point position)
         {
             switch (mouseAction)
@@ -207,30 +240,33 @@ namespace SketchAssistantWPF
         public void MouseEvent(MouseAction mouseAction, StrokeCollection strokes)
         {
 
-            switch (mouseAction)
+            if (!programModel.optiTrackInUse)
             {
-                case MouseAction.Down:
-                    programModel.MouseDown();
-                    break;
-                case MouseAction.Up:
-                    if (strokes.Count > 0)
-                    {
-                        StylusPointCollection sPoints = strokes.First().StylusPoints;
-                        List<Point> points = new List<Point>();
-                        foreach (StylusPoint p in sPoints)
-                            points.Add(new Point(p.X, p.Y));
-                        programModel.MouseUp(points);
-                    }
-                    else
-                    {
-                        programModel.MouseUp(true);
-                    }
-                    break;
-                case MouseAction.Up_Invalid:
-                    programModel.MouseUp(false);
-                    break;
-                default:
-                    break;
+                switch (mouseAction)
+                {
+                    case MouseAction.Down:
+                        programModel.StartNewLine();
+                        break;
+                    case MouseAction.Up:
+                        if (strokes.Count > 0)
+                        {
+                            StylusPointCollection sPoints = strokes.First().StylusPoints;
+                            List<Point> points = new List<Point>();
+                            foreach (StylusPoint p in sPoints)
+                                points.Add(new Point(p.X, p.Y));
+                            programModel.FinishCurrentLine(points);
+                        }
+                        else
+                        {
+                            programModel.FinishCurrentLine(true);
+                        }
+                        break;
+                    case MouseAction.Up_Invalid:
+                        programModel.FinishCurrentLine(false);
+                        break;
+                    default:
+                        break;
+                }
             }
         }
 
@@ -247,6 +283,18 @@ namespace SketchAssistantWPF
             return programView.GetCursorPosition();
         }
 
+        /// <summary>
+        /// Updates the currentline
+        /// </summary>
+        /// <param name="linepoints">The points of the current line.</param>
+        public void UpdateCurrentLine(List<Point> linepoints)
+        {
+            Polyline currentLine = new Polyline();
+            currentLine.Stroke = Brushes.Black;
+            currentLine.Points = new PointCollection(linepoints);
+            programView.DisplayCurrLine(currentLine);
+        }
+
         /// <summary>
         /// Clears all Lines in the right canvas.
         /// </summary>
@@ -287,12 +335,12 @@ namespace SketchAssistantWPF
             }
             //Calculate similarity scores 
             UpdateSimilarityScore(Double.NaN); var templist = lines.Where(tup => tup.Item1).ToList();
-            if (LeftLines.Count > 0)
+            if (leftLines.Count > 0)
             {
-                for (int i = 0; i < LeftLines.Count; i++)
+                for (int i = 0; i < leftLines.Count; i++)
                 {
                     if (templist.Count == i) break;
-                    UpdateSimilarityScore(GeometryCalculator.CalculateSimilarity(templist[i].Item2, LeftLines[i]));
+                    UpdateSimilarityScore(GeometryCalculator.CalculateSimilarity(templist[i].Item2, leftLines[i]));
                 }
             }
             else if (templist.Count > 1)
@@ -301,18 +349,6 @@ namespace SketchAssistantWPF
             }
         }
 
-        /// <summary>
-        /// Updates the currentline
-        /// </summary>
-        /// <param name="linepoints">The points of the current line.</param>
-        public void UpdateCurrentLine(List<Point> linepoints)
-        {
-            Polyline currentLine = new Polyline();
-            currentLine.Stroke = Brushes.Black;
-            currentLine.Points = new PointCollection(linepoints);
-            programView.DisplayCurrLine(currentLine);
-        }
-
         /// <summary>
         /// A function to update the displayed lines in the left canvas.
         /// </summary>
@@ -329,7 +365,7 @@ namespace SketchAssistantWPF
             programView.SetCanvasState("LeftCanvas", true);
             programView.SetCanvasState("RightCanvas", true);
 
-            LeftLines = lines;
+            leftLines = lines;
         }
 
         /// <summary>
@@ -341,23 +377,49 @@ namespace SketchAssistantWPF
         /// <param name="canRedo">If actions in the model can be redone</param>
         /// <param name="canvasActive">If the right canvas is active</param>
         /// <param name="graphicLoaded">If an image is loaded in the model</param>
-        public void UpdateUIState(bool inDrawingMode, bool canUndo, bool canRedo, bool canvasActive, bool graphicLoaded)
+        /// <param name="optiTrackAvailable">If there is an optitrack system available</param>
+        /// <param name="optiTrackInUse">If the optitrack system is active</param>
+        public void UpdateUIState(bool inDrawingMode, bool canUndo, bool canRedo, bool canvasActive,
+                bool graphicLoaded, bool optiTrackAvailable, bool optiTrackInUse)
         {
             Dictionary<String, MainWindow.ButtonState> dict = new Dictionary<String, MainWindow.ButtonState> {
                 {"canvasButton", MainWindow.ButtonState.Enabled }, {"drawButton", MainWindow.ButtonState.Disabled}, {"deleteButton",MainWindow.ButtonState.Disabled },
-                {"undoButton", MainWindow.ButtonState.Disabled },{"redoButton",  MainWindow.ButtonState.Disabled}};
+                {"undoButton", MainWindow.ButtonState.Disabled },{"redoButton",  MainWindow.ButtonState.Disabled}, {"drawWithOptiButton", MainWindow.ButtonState.Disabled}};
 
             if (canvasActive)
             {
                 if (inDrawingMode)
                 {
-                    dict["drawButton"] = MainWindow.ButtonState.Active;
-                    dict["deleteButton"] = MainWindow.ButtonState.Enabled;
+                    if (optiTrackAvailable)
+                    {
+                        if (optiTrackInUse)
+                        {
+                            dict["drawButton"] = MainWindow.ButtonState.Enabled;
+                            dict["drawWithOptiButton"] = MainWindow.ButtonState.Active;
+                            dict["deleteButton"] = MainWindow.ButtonState.Enabled;
+                        }
+                        else
+                        {
+                            dict["drawButton"] = MainWindow.ButtonState.Active;
+                            dict["drawWithOptiButton"] = MainWindow.ButtonState.Enabled;
+                            dict["deleteButton"] = MainWindow.ButtonState.Enabled;
+                        }
+                    }
+                    else
+                    {
+                        dict["drawButton"] = MainWindow.ButtonState.Active;
+                        dict["deleteButton"] = MainWindow.ButtonState.Enabled;
+                    }
+
                 }
                 else
                 {
                     dict["drawButton"] = MainWindow.ButtonState.Enabled;
                     dict["deleteButton"] = MainWindow.ButtonState.Active;
+                    if (optiTrackAvailable)
+                    {
+                        dict["drawWithOptiButton"] = MainWindow.ButtonState.Enabled;
+                    }
                 }
                 if (canUndo) { dict["undoButton"] = MainWindow.ButtonState.Enabled; }
                 if (canRedo) { dict["redoButton"] = MainWindow.ButtonState.Enabled; }
@@ -380,6 +442,17 @@ namespace SketchAssistantWPF
             programView.ShowInfoMessage(msg);
         }
 
+        /// <summary>
+        /// Pass-through function to desplay an Warning message in the view
+        /// </summary>
+        /// <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)
+        {
+            return programView.ShowWarning(msg);
+        }
+
         /// <summary>
         /// Pass-trough function to update the display of the last action taken.
         /// </summary>
@@ -398,6 +471,11 @@ namespace SketchAssistantWPF
             return programView.IsMousePressed();
         }
 
+        public void PassOptiTrackMessage(String stringToPass)
+        {
+            programView.SetOptiTrackText(stringToPass);
+        }
+
         /// <summary>
         /// Update the similarity score displayed in the UI.
         /// </summary>
@@ -407,13 +485,32 @@ namespace SketchAssistantWPF
         {
             if (Double.IsNaN(score))
             {
-                ImageSimilarity.Clear();
+                imageSimilarity.Clear();
                 programView.SetImageSimilarityText("");
             }
             else
             {
-                if (score >= 0 && score <= 1) ImageSimilarity.Add(score);
-                programView.SetImageSimilarityText((ImageSimilarity.Sum() / ImageSimilarity.Count).ToString());
+                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;
             }
         }
 
@@ -426,18 +523,20 @@ namespace SketchAssistantWPF
         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.5; xDif = 2.5; yDif = 2.5;
+                    shape = ((MainWindow)programView).overlayDictionary["optipoint"];
+                    visibility = 0.75; xDif = 2.5; yDif = 2.5;
                     break;
                 case "startpoint":
-                    shape = ((MainWindow)programView).OverlayDictionary["startpoint"];
+                    shape = ((MainWindow)programView).overlayDictionary["startpoint"];
                     xDif = ((MainWindow)programView).markerRadius; yDif = xDif;
                     break;
                 case "endpoint":
-                    shape = ((MainWindow)programView).OverlayDictionary["endpoint"];
+                    shape = ((MainWindow)programView).overlayDictionary["endpoint"];
                     xDif = ((MainWindow)programView).markerRadius; yDif = xDif;
                     break;
                 default:
@@ -446,7 +545,6 @@ namespace SketchAssistantWPF
             }
             if (visible) shape.Opacity = visibility;
             else shape.Opacity = 0.00001;
-            Point point = ConvertRightCanvasCoordinateToOverlay(position);
             shape.Margin = new Thickness(point.X - xDif, point.Y - yDif, 0, 0);
         }
 
@@ -463,9 +561,9 @@ namespace SketchAssistantWPF
             switch (name.Substring(0, 7))
             {
                 case "dotLine":
-                    if (((MainWindow)programView).OverlayDictionary.ContainsKey(name))
+                    if (((MainWindow)programView).overlayDictionary.ContainsKey(name))
                     {
-                        shape = ((MainWindow)programView).OverlayDictionary[name];
+                        shape = ((MainWindow)programView).overlayDictionary[name];
                         break;
                     }
                     goto default;
@@ -486,7 +584,7 @@ namespace SketchAssistantWPF
         public void MoveOptiPoint(Point position)
         {
             Point point = ConvertRightCanvasCoordinateToOverlay(position);
-            Shape shape = ((MainWindow)programView).OverlayDictionary["optipoint"];
+            Shape shape = ((MainWindow)programView).overlayDictionary["optipoint"];
             shape.Margin = new Thickness(point.X - 2.5, point.Y - 2.5, 0, 0);
         }
 

+ 6 - 0
SketchAssistant/SketchAssistantWPF/MVP_View.cs

@@ -122,6 +122,12 @@ namespace SketchAssistantWPF
         /// <returns>The cursor Position</returns>
         Point GetCursorPosition();
 
+        /// <summary>
+        /// Sets the contents of the last action taken indicator label.
+        /// </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.

+ 44 - 1
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" 
@@ -170,6 +170,47 @@
                     </Rectangle.Fill>
                 </Rectangle>
             </Button>
+            <ToggleButton x:Name="DrawWithOptiButton" ToolTip="Enter Drawing Mode (OptiTrack)" Click="DrawWithOptiButton_Click" BorderThickness="0">
+                <Rectangle Width="30" Height="30">
+                    <Rectangle.Fill>
+                        <DrawingBrush>
+                            <DrawingBrush.Drawing>
+                                <DrawingGroup Transform="1,0,0,1,-30.972436,-202.26931">
+                                    <GeometryDrawing Brush="#FF000000">
+                                        <GeometryDrawing.Pen>
+                                            <Pen Brush="#FF000000" Thickness="1.01090431" StartLineCap="Flat" EndLineCap="Flat" LineJoin="Miter" />
+                                        </GeometryDrawing.Pen>
+                                        <GeometryDrawing.Geometry>
+                                            <RectangleGeometry RadiusX="11.1847" RadiusY="11.1847" Rect="92.1615,202.7748,95.2826,53.7929" />
+                                        </GeometryDrawing.Geometry>
+                                    </GeometryDrawing>
+                                    <GeometryDrawing Brush="#FF000000">
+                                        <GeometryDrawing.Pen>
+                                            <Pen Brush="#FF000000" Thickness="2.57952762" StartLineCap="Flat" EndLineCap="Flat" LineJoin="Miter" />
+                                        </GeometryDrawing.Pen>
+                                        <GeometryDrawing.Geometry>
+                                            <RectangleGeometry RadiusX="32.2622" RadiusY="32.2622" Rect="32.2622,219.6902,215.0813,155.1658" />
+                                        </GeometryDrawing.Geometry>
+                                    </GeometryDrawing>
+                                    <GeometryDrawing>
+                                        <GeometryDrawing.Pen>
+                                            <Pen Brush="#FFFFFFFF" Thickness="10.62992096" StartLineCap="Flat" EndLineCap="Flat" LineJoin="Miter" />
+                                        </GeometryDrawing.Pen>
+                                        <GeometryDrawing.Geometry>
+                                            <EllipseGeometry RadiusX="48.8035" RadiusY="48.8035" Center="139.8029,297.2731" />
+                                        </GeometryDrawing.Geometry>
+                                    </GeometryDrawing>
+                                    <GeometryDrawing Brush="#FFFFFFFF">
+                                        <GeometryDrawing.Geometry>
+                                            <EllipseGeometry RadiusX="11.2372" RadiusY="11.2372" Center="66.1719,256.4501" />
+                                        </GeometryDrawing.Geometry>
+                                    </GeometryDrawing>
+                                </DrawingGroup>
+                            </DrawingBrush.Drawing>
+                        </DrawingBrush>
+                    </Rectangle.Fill>
+                </Rectangle>
+            </ToggleButton>
         </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"/>
@@ -183,6 +224,8 @@
                 <Separator/>
                 <TextBox Name="LastActionBox" Text="none" Background="LightGray"/>
                 <Separator/>
+                <TextBox Name="OptiTrackBox" Text="none" Background="LightGray"/>
+                <Separator/>
                 <TextBox Name="LineSimilarityBox" Text="-" Background="LightGray"/>
             </StatusBar>
         </DockPanel>

+ 55 - 18
SketchAssistant/SketchAssistantWPF/MainWindow.xaml.cs

@@ -1,4 +1,5 @@
 using Microsoft.Win32;
+using OptiTrack;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -48,7 +49,7 @@ 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
@@ -102,7 +103,7 @@ namespace SketchAssistantWPF
         /// <summary>
         /// Dictionary containing the overlay elements
         /// </summary>
-        public Dictionary<String, Shape> OverlayDictionary = new Dictionary<string, Shape>();
+        public Dictionary<String, Shape> overlayDictionary = new Dictionary<string, Shape>();
 
         /********************************************/
         /*** WINDOW SPECIFIC FUNCTIONS START HERE ***/
@@ -159,6 +160,26 @@ namespace SketchAssistantWPF
             RightCanvas.EditingMode = InkCanvasEditingMode.Ink;
         }
 
+        /// <summary>
+        /// Changes the state of the program to drawing with OptiTrack
+        /// </summary>
+        private void DrawWithOptiButton_Click(object sender, RoutedEventArgs e)
+        {
+            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>
         /// Hold left mouse button to start drawing.
         /// </summary>
@@ -172,15 +193,18 @@ namespace SketchAssistantWPF
         /// </summary>
         private void RightCanvas_MouseUp(object sender, MouseButtonEventArgs e)
         {
-            if (strokeCollection.Count == 0)
-            {
-                ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up_Invalid, strokeCollection);
-            }
-            else
+            if (ProgramPresenter.GetDrawingState())
             {
-                ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up, strokeCollection);
-                RightCanvas.Strokes.RemoveAt(0);
-                strokeCollection.RemoveAt(0);
+                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);
+                }
             }
         }
 
@@ -240,7 +264,11 @@ namespace SketchAssistantWPF
         private void SVGMenuItem_Click(object sender, RoutedEventArgs e)
         {
             if (ProgramPresenter.SVGToolStripMenuItemClick())
+            {
+                ProgramPresenter.NewCanvas();
                 RightCanvas.EditingMode = InkCanvasEditingMode.Ink;
+                RightCanvas.Strokes.Clear();
+            }
         }
 
         /*************************/
@@ -389,6 +417,14 @@ namespace SketchAssistantWPF
         }
 
         /// <summary>
+        /// Sets the contents of the last action taken indicator label.
+        /// </summary>
+        /// <param name="message">The new contents</param>
+        public void SetOptiTrackText(string message)
+        {
+            OptiTrackBox.Text = message;
+        }
+
         /// Sets the contents of the status bar label containing
         /// the similarity score of the left and right image.
         /// </summary>
@@ -428,6 +464,10 @@ namespace SketchAssistantWPF
                 case "redoButton":
                     buttonToChange = RedoButton;
                     break;
+                case "drawWithOptiButton":
+                    buttonToChange = DrawWithOptiButton;
+                    isToggleable = true;
+                    break;
                 default:
                     Console.WriteLine("Invalid Button was given to SetToolStripButton. \nMaybe you forgot to add a case?");
                     return;
@@ -539,18 +579,18 @@ namespace SketchAssistantWPF
             StartPointOverlay.Height = markerRadius * 2; StartPointOverlay.Width = markerRadius * 2;
             StartPointOverlay.Fill = Brushes.Green;
             StartPointOverlay.Effect = effect;
-            OverlayDictionary.Add("startpoint", StartPointOverlay);
+            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);
+            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);
+            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++)
             {
@@ -558,19 +598,16 @@ namespace SketchAssistantWPF
                 dotLine.Stroke = Brushes.Red;
                 dotLine.StrokeDashArray = new DoubleCollection { 2 + x, 2 + x };
                 dotLine.StrokeThickness = 1;
-                OverlayDictionary.Add("dotLine" + x.ToString(), dotLine);
+                overlayDictionary.Add("dotLine" + x.ToString(), dotLine);
             }
 
             //Common features of all overlay items
-            foreach (KeyValuePair<String, Shape> s in OverlayDictionary)
+            foreach (KeyValuePair<String, Shape> s in overlayDictionary)
             {
                 OverlayCanvas.Children.Add(s.Value);
                 s.Value.Opacity = 0.00001;
                 s.Value.IsHitTestVisible = false;
             }
-
-            //Enable optipoint initially
-            ProgramPresenter.SetOverlayStatus("optipoint", true, GetCursorPosition());
         }
 
         /// <summary>

BIN
SketchAssistant/SketchAssistantWPF/NPTrackingTools.dll


+ 195 - 0
SketchAssistant/SketchAssistantWPF/OptiTrackConnector.cs

@@ -0,0 +1,195 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+namespace OptiTrack
+{
+
+    public class OptiTrackConnector
+    {
+        private bool _stop = true;
+        private Thread _tracker;
+
+        public delegate void OnFrameReady(Frame frame);
+
+        public bool Init(String path)
+        {
+            int a = OptiTrackNativeWrapper.TT_Initialize();
+            int b = OptiTrackNativeWrapper.TT_LoadProject(path);
+            Console.WriteLine("a:" + a + ", b:" + b);
+            if (a + b != 0)
+            {
+                // breakpoint
+            }
+            return a + b == 0;
+        }
+
+        public void StartTracking(OnFrameReady frameReadyDelegate)
+        {
+            _stop = false;
+            _tracker = new Thread(delegate ()
+            {
+                HighPerformanceTimer hpt = new HighPerformanceTimer();
+                int i = 0;
+
+                hpt.Start();
+                while (!_stop)
+                {
+                    if (i++ == 200)
+                    {
+                        //Console.WriteLine("Tracker FPS: " + 200.0 / HPT.Stop());
+                        i = 0;
+                        hpt.Start();
+                    }
+                    OptiTrackNativeWrapper.TT_Update();
+
+                    frameReadyDelegate(BuildFrame());
+
+                    Thread.Sleep(15);
+                }
+            });
+            _tracker.Start();
+        }
+
+        Frame _lastFrame;
+
+        private Frame BuildFrame()
+        {
+            Frame frame = new Frame(OptiTrackNativeWrapper.TT_FrameMarkerCount(), OptiTrackNativeWrapper.TT_TrackableCount());
+
+            for (int i = 0; i < OptiTrackNativeWrapper.TT_FrameMarkerCount(); i++)
+            {
+                frame.Markers[i] = new Marker(
+                    OptiTrackNativeWrapper.TT_FrameMarkerX(i),
+                    OptiTrackNativeWrapper.TT_FrameMarkerY(i), 
+                    OptiTrackNativeWrapper.TT_FrameMarkerZ(i))
+                {
+                    BoundToTrackable = null
+                };
+            }
+
+            for (int i = 0; i < OptiTrackNativeWrapper.TT_TrackableCount(); i++)
+            {
+                Trackable t = new Trackable
+                {
+                    IsTracked = OptiTrackNativeWrapper.TT_IsTrackableTracked(i),
+                    Id = OptiTrackNativeWrapper.TT_TrackableID(i)
+                };
+
+                if (t.IsTracked)
+                {
+                    OptiTrackNativeWrapper.TT_TrackableLocation(i, out t.X, out t.Y, out t.Z, out t.Qx, out t.Qy, out t.Qz, out t.Qw,
+                                                             out t.Pitch, out t.Yaw, out t.Roll);
+
+                    if(_lastFrame != null)
+                    {
+                        if (_lastFrame.Trackables[i].IsAlmostSameCoordinates(t))
+                            t.IsTracked = false;
+                    }
+
+                    /*for (int j = 0; j < OptiTrackNativeWrapper.TT_TrackableMarkerCount(i); j++)
+                    {
+                        float markerRadius = trackableMarker(i, j);
+
+                        frame.markers[boundTrackable(frame, markerRadius,t)].boundToTrackable = t;
+                    }*/
+
+                }
+
+                frame.Trackables[i] = t;
+            }
+            _lastFrame = frame;
+
+            return frame;
+        }
+
+        /*private float trackableMarker(int trackableId, int markerId)
+        {
+            Marker tempMarker = new Marker(0, 0, 0);
+
+            OptiTrackNativeWrapper.TT_TrackableMarker(trackableId, markerId, out tempMarker.x, out tempMarker.y, out tempMarker.z);
+
+            float markerRadius = (float)Math.Pow(((float)Math.Pow(tempMarker.x, 2.00) + (float)Math.Pow(tempMarker.y, 2.00) + (float)Math.Pow(tempMarker.z, 2.00)),0.50);
+            markerRadius += (float)0.01;
+
+            return markerRadius;
+        }
+
+        private int boundTrackable(Frame frame, float markerRadius, Trackable trackable)
+        {
+            List<float> distanceValues = new List<float>();
+
+            for (int k = 0; k < frame.markers.Count(); k++)
+                distanceValues.Add((float)Math.Pow(((float)Math.Pow(trackable.x - frame.markers[k].x, 2.00) + (float)Math.Pow(trackable.y - frame.markers[k].y, 2.00) + (float)Math.Pow(trackable.z - frame.markers[k].z, 2.00)),0.50));
+
+            float minimumDistance = markerRadius;
+            
+            int boundMarkerId = 0;
+
+            for (int i = 0; i < distanceValues.Count; ++i)
+            {
+               
+                if (distanceValues[i] <= minimumDistance)
+                {
+                    minimumDistance = distanceValues[i];
+                    boundMarkerId = i;
+                }                           
+            }
+
+            return boundMarkerId;
+        }*/
+
+        public void StopTracking()
+        {
+            _stop = true;
+            OptiTrackNativeWrapper.TT_Shutdown();
+        }
+    }
+
+    
+    class OptiTrackNativeWrapper
+    {
+        [DllImport("NPTrackingTools.dll", CallingConvention = CallingConvention.Cdecl)]
+        public static extern int TT_Initialize();
+
+        [DllImport("NPTrackingTools.dll", CallingConvention = CallingConvention.Cdecl)]
+        public static extern int TT_Shutdown();
+
+        [DllImport("NPTrackingTools.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
+        public static extern int TT_LoadProject([MarshalAs(UnmanagedType.LPStr)]string file);
+
+        [DllImport("NPTrackingTools.dll", CallingConvention = CallingConvention.Cdecl)]
+        public static extern int TT_Update();
+
+        [DllImport("NPTrackingTools.dll", CallingConvention = CallingConvention.Cdecl)]
+        public static extern int TT_FrameMarkerCount();
+
+        [DllImport("NPTrackingTools.dll", CallingConvention = CallingConvention.Cdecl)]
+        public static extern int TT_TrackableCount();
+
+        [DllImport("NPTrackingTools.dll", CallingConvention = CallingConvention.Cdecl)]
+        public static extern int TT_TrackableLocation([In]int index, [Out]out float x, [Out]out float y, [Out]out float z, [Out]out float qx, [Out]out float qy, [Out]out float qz, [Out]out float qw, [Out]out float yaw, [Out]out float pitch, [Out]out float roll);
+
+        [DllImport("NPTrackingTools.dll", CallingConvention = CallingConvention.Cdecl)]
+        public static extern void TT_TrackableMarker([In]int rigidIndex, [In]int markerIndex, [Out]out float x, [Out]out float y, [Out]out float z);
+
+        [DllImport("NPTrackingTools.dll", CallingConvention = CallingConvention.Cdecl)]
+        public static extern float TT_FrameMarkerX([In]int index);
+
+        [DllImport("NPTrackingTools.dll", CallingConvention = CallingConvention.Cdecl)]
+        public static extern float TT_FrameMarkerY([In]int index);
+
+        [DllImport("NPTrackingTools.dll", CallingConvention = CallingConvention.Cdecl)]
+        public static extern float TT_FrameMarkerZ([In]int index);
+
+        [DllImport("NPTrackingTools.dll", CallingConvention = CallingConvention.Cdecl)]
+        public static extern bool TT_IsTrackableTracked([In]int index);
+
+        [DllImport("NPTrackingTools.dll", CallingConvention = CallingConvention.Cdecl)]
+        public static extern int TT_TrackableID(int index);
+
+        [DllImport("NPTrackingTools.dll", CallingConvention = CallingConvention.Cdecl)]
+        public static extern int TT_TrackableMarkerCount(int index);
+
+    }
+}

+ 39 - 0
SketchAssistant/SketchAssistantWPF/SketchAssistantWPF.csproj

@@ -52,6 +52,26 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>bin\x86\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>true</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
+    <OutputPath>bin\x86\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <Prefer32Bit>true</Prefer32Bit>
+  </PropertyGroup>
   <PropertyGroup>
     <ApplicationIcon>nicubunu-Quill.ico</ApplicationIcon>
   </PropertyGroup>
@@ -71,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">
@@ -78,11 +101,16 @@
       <SubType>Designer</SubType>
     </ApplicationDefinition>
     <Compile Include="ActionHistory.cs" />
+    <Compile Include="Wristband.cs" />
+    <Compile Include="CustomCanvas.cs" />
     <Compile Include="DebugData.cs" />
     <Compile Include="FileImporter.cs" />
     <Compile Include="FileImporterException.cs" />
+    <Compile Include="Frame.cs" />
     <Compile Include="GeometryCalculator.cs" />
+    <Compile Include="HighPerformanceTimer.cs" />
     <Compile Include="ImageDimension.cs" />
+    <Compile Include="OptiTrackConnector.cs" />
     <Compile Include="SketchAction.cs" />
     <Page Include="MainWindow.xaml">
       <Generator>MSBuild:Compile</Generator>
@@ -119,6 +147,9 @@
       <Generator>ResXFileCodeGenerator</Generator>
       <LastGenOutput>Resources.Designer.cs</LastGenOutput>
     </EmbeddedResource>
+    <None Include="optitrack_setup.ttp">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
     <None Include="packages.config" />
     <None Include="Properties\Settings.settings">
       <Generator>SettingsSingleFileGenerator</Generator>
@@ -140,6 +171,14 @@
       <Install>false</Install>
     </BootstrapperPackage>
   </ItemGroup>
+  <ItemGroup>
+    <Content Include="NPTrackingTools.dll">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+  <ItemGroup>
+    <WCFMetadata Include="Connected Services\" />
+  </ItemGroup>
   <ItemGroup>
     <Resource Include="nicubunu-Quill.ico" />
   </ItemGroup>

+ 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/optitrack_setup.ttp


+ 1 - 0
SketchAssistant/SketchAssistantWPF/packages.config

@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
+  <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.15" targetFramework="net461" />

+ 26 - 8
SketchAssistant/WhiteTests/UITest.cs

@@ -87,10 +87,28 @@ namespace WhiteTests
 
         public Window setupapp()
         {
+            string[] files;
+            Regex rx = new Regex(@"^(.*\\SketchAssistant\\)");
+            Match match = rx.Match(TestContext.DeploymentDirectory);
+            String SketchAssistDir = match.Groups[1].Value;
+            try
+            {
+                files = Directory.GetFiles(SketchAssistDir + @"SketchAssistantWPF\bin\", "SketchAssistantWPF.exe", SearchOption.AllDirectories);
+            }
+            catch
+            {
+                Regex rx_0 = new Regex(@"^(.*\\projects\\)");
+                Match match_0 = rx_0.Match(TestContext.DeploymentDirectory);
+                String ProjectsDir = match_0.Groups[1].Value;
+                files = Directory.GetFiles(ProjectsDir, "SketchAssistantWPF.exe", SearchOption.AllDirectories);
+            }
+
+            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");
+            ProcessStartInfo processStart = new ProcessStartInfo(app_path, "-debug");*/
             application = Application.Launch(processStart);
             return application.GetWindow("Sketch Assistant");
         }
@@ -130,7 +148,7 @@ namespace WhiteTests
             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.MoveMouseBy(100, 100);
             inputSimulator.Mouse.LeftButtonDown();
             Thread.Sleep(30);
             inputSimulator.Mouse.MoveMouseBy(100, 100);
@@ -978,28 +996,28 @@ namespace WhiteTests
 
         [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 }, 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 }, 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 < 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)
+                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);
+                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)
                 {

+ 0 - 3
SketchAssistant/WhiteTests/packages.config

@@ -2,10 +2,7 @@
 <packages>
   <package id="Castle.Core" version="4.3.1" targetFramework="net461" />
   <package id="InputSimulator" version="1.0.4.0" targetFramework="net461" />
-  <package id="Microsoft.TestPlatform" version="16.0.1" targetFramework="net461" />
   <package id="MSTest.TestAdapter" version="1.4.0" targetFramework="net461" />
   <package id="MSTest.TestFramework" version="1.4.0" targetFramework="net461" />
-  <package id="OpenCover" version="4.7.922" targetFramework="net461" />
-  <package id="ReportGenerator" version="4.0.15" targetFramework="net461" />
   <package id="TestStack.White" version="0.13.3" targetFramework="net461" />
 </packages>

BIN
SketchAssistant/optitrack_setup.ttp


+ 13 - 0
userstory20.md

@@ -0,0 +1,13 @@
+# Userstory 20 
+ 
+|**ID**|20|  
+|-|-|
+|**Name**|Tracken des Fingerpoints|
+|**Beschreibung**|Als Auftraggeber wünsche ich, dass das die Position des Cursors nicht mehr durch das berühren der Videowall bestimmt wird, sondern durch einen am Finger angebrachten Trackingpoint.|
+|**Akzeptanzkriterium**|Die Position des Cursors muss mithilfe des Motion Caputre Systems bestimmt werden. Das heißt, dass der Zeichenweg durch die Bewegung der Hand bestimmt wird.|
+|Geschätzter Aufwand (Story Points)|keine|
+|Entwickler|Rumei, Dennis|
+|Umgesetzt in Iteration|keine|
+|Tatsächlicher Aufwand (Std.)|keine|
+|Velocity (Std./Story Point)|keine|
+|Bemerkungen|Keine|

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