Browse Source

Userstory 13 & 17 (#8)

Finished Userstory 13 & 17
m-edlund 5 years ago
parent
commit
2bb3e58115

+ 13 - 0
Finished Userstories/userstory12.md

@@ -0,0 +1,13 @@
+# Userstory 12  
+ 
+|**ID**|12|  
+|-|-|
+|**Name**|Darstellen einer Zeichnung in der linken Picture Box|
+|**Beschreibung**|In der Linken picture box kann ein Bild angezeigt werden, was Line Elemente des Programms verwendet.|
+|**Akzeptanzkriterium**|Die Userstory ist akzeptiert, wenn das Programm ein Bild in bestehend aus den internen Line Elementen in der linken picture box darstellen kann.|
+|Geschätzter Aufwand (Story Points)|6|
+|Entwickler|Vincenz|
+|Umgesetzt in Iteration|3|
+|Tatsächlicher Aufwand (Std.)|8|
+|Velocity (Std./Story Point)|1.333|
+|Bemerkungen|Keine|

+ 13 - 0
Finished Userstories/userstory13.md

@@ -0,0 +1,13 @@
+# Userstory 13 
+ 
+|**ID**|13|  
+|-|-|
+|**Name**|Start und Endpunkt anzeigen|
+|**Beschreibung**|Beim Nachzeichnen kann der Start- & Endpunkt einer nachzuzeichnenden Linie angzeigt werden.|
+|**Akzeptanzkriterium**|Sofern auf der linken Seite eine nachzeichenbare Grafik dargestellt wird, kann der Start- und Endpunkt der ersten Linie der linken Grafik an der richtigen Stelle auf der rechten Seite angezeigt werden.|
+|Geschätzter Aufwand (Story Points)|2|
+|Entwickler|Martin|
+|Umgesetzt in Iteration|4|
+|Tatsächlicher Aufwand (Std.)|1|
+|Velocity (Std./Story Point)|0.5|
+|Bemerkungen|Keine|

+ 13 - 0
Finished Userstories/userstory17.md

@@ -0,0 +1,13 @@
+# Userstory 17  
+ 
+|**ID**|17|  
+|-|-|
+|**Name**|Nachzeichenmodus|
+|**Beschreibung**|Es gibt einen speziellen Modus zum Nachzeichnen von Bildern.|
+|**Akzeptanzkriterium**|Sobald ein Bild geladen wird, welches sich nachzeichenen lässt, wird der Nachzeichenmodus aktiviert. Ist der Nachzeichen Modus aktiviert, so werden Startpunkte für alle Linien auf der rechten Seite angezeigt. Fängt der Nutzer an einem Startpunkt an zu zeichnen, werden die anderen Startpunkte ausgeblendet und der Endpunkt der aktuellen Linie angezeigt. Zeichnet der Nutzer die Linie bis zum Endpunkt fertig, werden die verbleibenden Startpunkte angezeigt und der Nutzer kann eine neue Linie nachzeichnen. Wenn der Nutzer die Linie nicht zuende Zeichnet, muss sie von vorne begonnen werden.|
+|Geschätzter Aufwand (Story Points)|10|
+|Entwickler|Martin|
+|Umgesetzt in Iteration|4|
+|Tatsächlicher Aufwand (Std.)|11|
+|Velocity (Std./Story Point)|1.1|
+|Bemerkungen|Keine|

+ 230 - 8
SketchAssistant/SketchAssistant.Tests/UnitTest1.cs

@@ -20,7 +20,7 @@ namespace Tests
             //Test point
             List<Point> expectedResult = new List<Point>();
             expectedResult.Add(new Point(1, 2));
-            List<Point> actualResult = SketchAssistant.Line.BresenhamLineAlgorithm(new Point(1, 2), 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++)
             {
@@ -34,7 +34,7 @@ namespace Tests
             //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.Line.BresenhamLineAlgorithm(new Point(1, 2), new Point(6, 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++)
             {
@@ -48,7 +48,7 @@ namespace Tests
             //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.Line.BresenhamLineAlgorithm(new Point(6, 2), new Point(1, 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++)
             {
@@ -62,7 +62,7 @@ namespace Tests
             //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.Line.BresenhamLineAlgorithm(new Point(7, 5), new Point(7, 25));
+            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++)
             {
@@ -76,7 +76,7 @@ namespace Tests
             //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.Line.BresenhamLineAlgorithm(new Point(7, 25), new Point(7, 5));
+            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++)
             {
@@ -90,7 +90,7 @@ namespace Tests
             //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.Line.BresenhamLineAlgorithm(new Point(7, 5), new Point(27, 25));
+            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++)
             {
@@ -104,7 +104,7 @@ namespace Tests
             //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.Line.BresenhamLineAlgorithm(new Point(27, 25), new Point(7, 5));
+            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++)
             {
@@ -367,7 +367,229 @@ namespace Tests
         private List<Line> GetLeftImage(Form1 program)
         {
             //cast is save as long as Form1#GetAllVariables() is conform to its contract
-            return (List<Line>) program.GetAllVariables()[4];
+            return (List<Line>)program.GetAllVariables().Find(x => x.Item1.Equals("leftLineList")).Item2;
+        }
+    }
+
+    [TestClass]
+    public class RedrawAssistantTests
+    {
+        private RedrawAssistant GetAssistant(List<Line> input)
+        {
+            if(input.Count == 0)
+            {
+                return new RedrawAssistant();
+            }
+            else
+            {
+                return new RedrawAssistant(input);
+            }
+        }
+
+        [DataTestMethod]
+        [DataRow(17, 20, new int[] { 54, 43, 57, 11, 145, 34, 113, 299, 0 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, true)]
+        [DataRow(-50, 30, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, false)]
+        [DataRow(-70, -20, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 54, 43, 57, 11, 145, 34, 113, 199, 0 }, true)]
+        public void InactiveRedrawAssistantTest(int x, int y, int[] xCoords, int[] yCoords, bool lineActive)
+        {
+            RedrawAssistant testAssistant = GetAssistant(new List<Line>());
+            List<Point> testPoints = new List<Point>();
+            for (int i = 0; i < xCoords.Length; i++)
+            {
+                testPoints.Add(new Point(xCoords[i], yCoords[i]));
+            }
+            List<Tuple<bool, Line>> testLines = new List<Tuple<bool, Line>> { new Tuple<bool, Line>(lineActive, new Line(testPoints, 0)) };
+            List<HashSet<Point>> result = testAssistant.Tick(new Point(1, 1), testLines, -1, false);
+            Assert.AreEqual(0, result.Count);
+        }
+
+        [DataTestMethod]
+        [DataRow(17, 20, new int[] { 54, 43, 57, 11, 145, 34, 113, 299, 0 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, 1)]
+        [DataRow(-50, 30, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, 1)]
+        [DataRow(33, 54, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 54, 43, 57, 11, 145, 34, 113, 199, 0 }, 2)]
+        public void ActiveRedrawAssistantTestStartedDrawing(int x, int y, int[] xCoords, int[] yCoords, int resultingCount)
+        {
+            List<Point> testPoints = new List<Point>();
+            for (int i = 0; i < xCoords.Length; i++)
+            {
+                testPoints.Add(new Point(xCoords[i], yCoords[i]));
+            }
+            List<Tuple<bool, Line>> testInputLines = new List<Tuple<bool, Line>>();
+            List<Line> testRedrawLines = new List<Line> { new Line(testPoints, 0) };
+            RedrawAssistant testAssistant = GetAssistant(testRedrawLines);
+            //Setting Marker Radius to 1 so that only the functionality of the RedrawAssistant is checked 
+            //and not the functionality of the Circle algorithm.
+            testAssistant.SetMarkerRadius(1);
+            List<HashSet<Point>> tickResult = testAssistant.Tick(new Point(x, y), testInputLines, -1, false);
+
+
+            Assert.AreEqual(resultingCount, tickResult.Count);
+            if(resultingCount == 1)
+            {
+                foreach(Point p in tickResult[0])
+                {
+                    Assert.AreEqual(testPoints[0], p);
+                }
+            }
+            if(resultingCount == 2)
+            {
+                foreach (Point p in tickResult[0])
+                {
+                    Assert.AreEqual(testPoints[0], p);
+                }
+                foreach (Point p in tickResult[1])
+                {
+                    Assert.AreEqual(testPoints[testPoints.Count-1], p);
+                }
+            }
+        }
+
+        [DataTestMethod]
+        [DataRow(17, 20, new int[] { 54, 43, 57, 11, 145, 34, 113, 299, 0 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 },
+             new int[] { 77, 20, 3, 74, 28 }, new int[] { 40, 50, 20, 77, 28}, 2, false)]
+        [DataRow(33, 33, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 },
+             new int[] { 42, 140, 30, 30, 30 }, new int[] { 11, 145, 34, 113, 28 }, 2, true)]
+        [DataRow(33, 54, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 54, 43, 57, 11, 145, 34, 113, 199, 0 },
+             new int[] { 43, 57, 11, 145 }, new int[] { 33, 42, 140, 30 }, 2, true)]
+        public void ActiveRedrawAssistantTestMultipleLinesRedrawn(
+            int x, int y, int[] xCoords_one, int[] yCoords_one, int[] xCoords_two, int[] yCoords_two, int resultingCount, bool showingStartAndEnd)
+        {
+            List<Point> testPoints1 = new List<Point>();
+            for (int i = 0; i < xCoords_one.Length; i++)
+            {
+                testPoints1.Add(new Point(xCoords_one[i], yCoords_one[i]));
+            }
+            List<Point> testPoints2 = new List<Point>();
+            for (int i = 0; i < xCoords_two.Length; i++)
+            {
+                testPoints2.Add(new Point(xCoords_two[i], yCoords_two[i]));
+            }
+            List<Tuple<bool, Line>> testInputLines = new List<Tuple<bool, Line>>();
+
+            List<Line> testRedrawLines = new List<Line> { new Line(testPoints1, 0) , new Line(testPoints2, 1) };
+            RedrawAssistant testAssistant = GetAssistant(testRedrawLines);
+            //Setting Marker Radius to 1 so that only the functionality of the RedrawAssistant is checked 
+            //and not the functionality of the Circle algorithm.
+            testAssistant.SetMarkerRadius(1);
+
+            List<HashSet<Point>> tickResult = testAssistant.Tick(new Point(x, y), testInputLines, -1, false);
+
+            Assert.AreEqual(resultingCount, tickResult.Count);
+            if (showingStartAndEnd)
+            {
+                foreach (Point p in tickResult[0])
+                {
+                    Assert.AreEqual(testPoints1[0], p);
+                }
+                foreach (Point p in tickResult[1])
+                {
+                    Assert.AreEqual(testPoints1[testPoints1.Count - 1], p);
+                }
+            }
+            else
+            {
+                foreach (Point p in tickResult[0])
+                {
+                    Assert.AreEqual(testPoints1[0], p);
+                }
+                foreach (Point p in tickResult[1])
+                {
+                    Assert.AreEqual(testPoints2[0], p);
+                }
+            }
+        }
+
+        [DataTestMethod]
+        [DataRow(17, 20, 17, 20, -1 ,false, new int[] { 54, 43, 57, 11, 145, 34, 113, 299, 0 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 },
+     new int[] { 77, 20, 3, 74, 28 }, new int[] { 40, 50, 20, 77, 28 }, 2, 2, false, false)]
+
+        [DataRow(33, 33, 2, 2, 0, true, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 },
+     new int[] { 42, 140, 30, 30, 30 }, new int[] { 11, 145, 34, 113, 28 }, 2, 1, true, false)]
+
+        [DataRow(33, 54, 17, 0, 0, true, new int[] { 33, 42, 140, 30, 30, 30, 32, 145, 2 }, new int[] { 54, 43, 57, 11, 145, 34, 113, 199, 0 },
+     new int[] { 43, 57, 11, 145 }, new int[] { 33, 42, 140, 30 }, 2, 2, true, false)]
+        public void ActiveRedrawAssistantTestLineFinished(
+            int x_one, int y_one, int x_two, int y_two, int lineID, bool finishedDrawing, int[] xCoords_one, int[] yCoords_one, int[] xCoords_two, int[] yCoords_two, 
+            int count_one, int count_two, bool showingSE_one, bool showingSE_two)
+        {
+            List<Point> testPoints1 = new List<Point>();
+            for (int i = 0; i < xCoords_one.Length; i++)
+            {
+                testPoints1.Add(new Point(xCoords_one[i], yCoords_one[i]));
+            }
+            List<Point> testPoints2 = new List<Point>();
+            for (int i = 0; i < xCoords_two.Length; i++)
+            {
+                testPoints2.Add(new Point(xCoords_two[i], yCoords_two[i]));
+            }
+            List<Tuple<bool, Line>> testInputLines = new List<Tuple<bool, Line>>();
+
+            List<Line> testRedrawLines = new List<Line> { new Line(testPoints1, 0), new Line(testPoints2, 1) };
+            RedrawAssistant testAssistant = GetAssistant(testRedrawLines);
+            //Setting Marker Radius to 1 so that only the functionality of the RedrawAssistant is checked 
+            //and not the functionality of the Circle algorithm.
+            testAssistant.SetMarkerRadius(1);
+
+            List<HashSet<Point>> tickResult1 = testAssistant.Tick(new Point(x_one, y_one), testInputLines, lineID, false);
+            List<HashSet<Point>> tickResult2 = testAssistant.Tick(new Point(x_two, y_two), testInputLines, lineID, finishedDrawing);
+
+            Assert.AreEqual(count_one, tickResult1.Count);
+            if (showingSE_one)
+            {
+                foreach(Point p in tickResult1[0])
+                {
+                    Assert.AreEqual(testPoints1[0], p);
+                }
+                foreach (Point p in tickResult1[1])
+                {
+                    Assert.AreEqual(testPoints1[testPoints1.Count - 1], p);
+                }
+            }
+            else
+            {
+                foreach (Point p in tickResult1[0])
+                {
+                    Assert.AreEqual(testPoints1[0], p);
+                }
+                foreach (Point p in tickResult1[1])
+                {
+                    Assert.AreEqual(testPoints2[0], p);
+                }
+            }
+
+            Assert.AreEqual(count_two, tickResult2.Count);
+            if(count_two == 2)
+            {
+                if (showingSE_two)
+                {
+                    foreach (Point p in tickResult2[0])
+                    {
+                        Assert.AreEqual(testPoints1[0], p);
+                    }
+                    foreach (Point p in tickResult2[1])
+                    {
+                        Assert.AreEqual(testPoints1[testPoints1.Count - 1], p);
+                    }
+                }
+                else
+                {
+                    foreach (Point p in tickResult2[0])
+                    {
+                        Assert.AreEqual(testPoints1[0], p);
+                    }
+                    foreach (Point p in tickResult2[1])
+                    {
+                        Assert.AreEqual(testPoints2[0], p);
+                    }
+                }
+            }
+            if(count_two == 1)
+            {
+                foreach (Point p in tickResult2[0])
+                {
+                    Assert.AreEqual(testPoints2[0], p);
+                }
+            }
         }
     }
 }

+ 265 - 86
SketchAssistant/SketchAssistant/Form1.cs

@@ -91,18 +91,33 @@ namespace SketchAssistant
         /// </summary>
         Graphics rightGraph = null;
         /// <summary>
-        /// Deletion Matrixes for checking postions of lines in the image
+        /// Lookup Matrix for checking postions of lines in the image
         /// </summary>
         bool[,] isFilledMatrix;
+        /// <summary>
+        /// Lookup Matrix for getting line ids at a certain postions of the image
+        /// </summary>
         HashSet<int>[,] linesMatrix;
         /// <summary>
         /// Size of deletion area
         /// </summary>
-        uint deletionSize = 2;
+        int deletionRadius = 2;
         /// <summary>
         /// History of Actions
         /// </summary>
         ActionHistory historyOfActions;
+        /// <summary>
+        /// List of items which will be overlayed over the right canvas.
+        /// </summary>
+        List<HashSet<Point>> overlayItems;
+        /// <summary>
+        /// The assistant responsible for the redraw mode
+        /// </summary>
+        RedrawAssistant redrawAss;
+        /// <summary>
+        /// Size of areas marking endpoints of lines in the redraw mode.
+        /// </summary>
+        int markerRadius = 10;
 
         /******************************************/
         /*** FORM SPECIFIC FUNCTIONS START HERE ***/
@@ -113,13 +128,17 @@ namespace SketchAssistant
             currentState = ProgramState.Idle;
             this.DoubleBuffered = true;
             historyOfActions = new ActionHistory(null);
+            redrawAss = new RedrawAssistant();
             UpdateButtonStatus();
         }
 
-        //Resize Function connected to the form resize event, will refresh the form when it is resized
+        /// <summary>
+        /// Resize Function connected to the form resize event, will refresh the form when it is resized
+        /// </summary>
         private void Form1_Resize(object sender, System.EventArgs e)
         {
             this.Refresh();
+            UpdateSizes();
         }
         
         //Load button, will open an OpenFileDialog
@@ -142,25 +161,43 @@ namespace SketchAssistant
         /// </summary>
         private void examplePictureToolStripMenuItem_Click(object sender, EventArgs e)
         {
-            openFileDialog.Filter = "Interactive Sketch-Assistant Drawing|*.isad";
-            if (openFileDialog.ShowDialog() == DialogResult.OK)
+            if (CheckSavedStatus())
             {
-                toolStripLoadStatus.Text = openFileDialog.SafeFileName;
-                try
-                {
-                    (int, int, List<Line>) values = fileImporter.ParseISADInputFile(openFileDialog.FileName);
-                    DrawEmptyCanvasLeft(values.Item1, values.Item2);
-                    BindAndDrawLeftImage(values.Item3);
-                    this.Refresh();
-                }
-                catch(FileImporterException ex)
+                openFileDialog.Filter = "Interactive Sketch-Assistant Drawing|*.isad";
+                if (openFileDialog.ShowDialog() == DialogResult.OK)
                 {
-                    ShowInfoMessage(ex.ToString());
+                    toolStripLoadStatus.Text = openFileDialog.SafeFileName;
+                    try
+                    {
+                        (int, int, List<Line>) values = fileImporter.ParseISADInputFile(openFileDialog.FileName);
+                        DrawEmptyCanvasLeft(values.Item1, values.Item2);
+                        BindAndDrawLeftImage(values.Item3);
+
+                        //Match The right canvas to the left
+                        historyOfActions = new ActionHistory(lastActionTakenLabel);
+                        DrawEmptyCanvasRight();
+                        isFilledMatrix = new bool[rightImage.Width, rightImage.Height];
+                        linesMatrix = new HashSet<int>[rightImage.Width, rightImage.Height];
+                        rightLineList = new List<Tuple<bool, Line>>();
+                        //Start the redraw mode
+                        redrawAss = new RedrawAssistant(leftLineList);
+                        UpdateSizes();
+                        overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, -1, false);
+                        RedrawRightImage();
+                        this.Refresh();
+                    }
+                    catch (FileImporterException ex)
+                    {
+                        ShowInfoMessage(ex.ToString());
+                    }
                 }
             }
+            UpdateButtonStatus();
         }
 
-        //Changes the state of the program to drawing
+        /// <summary>
+        /// Changes the state of the program to drawing
+        /// </summary>
         private void drawButton_Click(object sender, EventArgs e)
         {
             if(rightImage != null)
@@ -177,7 +214,9 @@ namespace SketchAssistant
             UpdateButtonStatus();
         }
 
-        //Changes the state of the program to deletion
+        /// <summary>
+        /// Changes the state of the program to deletion
+        /// </summary>
         private void deleteButton_Click(object sender, EventArgs e)
         {
             if (rightImage != null)
@@ -194,7 +233,9 @@ namespace SketchAssistant
             UpdateButtonStatus();
         }
 
-        //Undo an action
+        /// <summary>
+        /// Undo an Action.
+        /// </summary>
         private void undoButton_Click(object sender, EventArgs e)
         {
             if (historyOfActions.CanUndo())
@@ -214,12 +255,16 @@ namespace SketchAssistant
                     default:
                         break;
                 }
+                overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, -1, false);
+                RedrawRightImage();
             }
             historyOfActions.MoveAction(true);
             UpdateButtonStatus();
         }
 
-        //Redo an action
+        /// <summary>
+        /// Redo an Action.
+        /// </summary>
         private void redoButton_Click(object sender, EventArgs e)
         {
             if (historyOfActions.CanRedo())
@@ -240,11 +285,15 @@ namespace SketchAssistant
                     default:
                         break;
                 }
+                overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, -1, false);
+                RedrawRightImage();
             }
             UpdateButtonStatus();
         }
 
-        //Detect Keyboard Shortcuts
+        /// <summary>
+        /// Detect Keyboard Shortcuts.
+        /// </summary>
         private void Form1_KeyDown(object sender, KeyEventArgs e)
         {
             if (e.Modifiers == Keys.Control && e.KeyCode == Keys.Z)
@@ -257,13 +306,17 @@ namespace SketchAssistant
             }
         }
 
-        //get current Mouse positon within the right picture box
+        /// <summary>
+        /// Get current Mouse positon within the right picture box.
+        /// </summary>
         private void pictureBoxRight_MouseMove(object sender, MouseEventArgs e)
         {
             currentCursorPosition = ConvertCoordinates(new Point(e.X, e.Y));
         }
-
-        //hold left mouse button to draw.
+        
+        /// <summary>
+        /// Hold left mouse button to start drawing.
+        /// </summary>
         private void pictureBoxRight_MouseDown(object sender, MouseEventArgs e)
         {
             mousePressed = true;
@@ -272,8 +325,10 @@ namespace SketchAssistant
                 currentLine = new List<Point>();
             }
         }
-
-        //Lift left mouse button to stop drawing and add a new Line.
+        
+        /// <summary>
+        /// Lift left mouse button to stop drawing and add a new Line.
+        /// </summary>
         private void pictureBoxRight_MouseUp(object sender, MouseEventArgs e)
         {
             mousePressed = false;
@@ -283,29 +338,21 @@ namespace SketchAssistant
                 rightLineList.Add(new Tuple<bool, Line>(true, newLine));
                 newLine.PopulateMatrixes(isFilledMatrix, linesMatrix);
                 historyOfActions.AddNewAction(new SketchAction(SketchAction.ActionType.Draw, newLine.GetID()));
+                //Execute a RedrawAssistant tick with the currently finished Line
+                overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, newLine.GetID(), true);
+                RedrawRightImage();
             }
             UpdateButtonStatus();
         }
-
-        //Button to create a new Canvas. Will create an empty image 
-        //which is the size of the left image, if there is one.
-        //If there is no image loaded the canvas will be the size of the right picture box
+        
+        /// <summary>
+        /// Button to create a new Canvas. Will create an empty image 
+        /// which is the size of the left image, if there is one.
+        /// If there is no image loaded the canvas will be the size of the right picture box
+        /// </summary>
         private void canvasButton_Click(object sender, EventArgs e)
         {
-            if (!historyOfActions.IsEmpty())
-            {
-                if (MessageBox.Show("You have unsaved changes, creating a new canvas will discard these.", 
-                    "Attention", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK)
-                {
-                    historyOfActions = new ActionHistory(lastActionTakenLabel);
-                    DrawEmptyCanvasRight();
-                    //The following lines cannot be in DrawEmptyCanvas()
-                    isFilledMatrix = new bool[rightImage.Width, rightImage.Height];
-                    linesMatrix = new HashSet<int>[rightImage.Width, rightImage.Height];
-                    rightLineList = new List<Tuple<bool, Line>>();
-                }
-            }
-            else
+            if (CheckSavedStatus())
             {
                 historyOfActions = new ActionHistory(lastActionTakenLabel);
                 DrawEmptyCanvasRight();
@@ -313,28 +360,47 @@ namespace SketchAssistant
                 isFilledMatrix = new bool[rightImage.Width, rightImage.Height];
                 linesMatrix = new HashSet<int>[rightImage.Width, rightImage.Height];
                 rightLineList = new List<Tuple<bool, Line>>();
+                //Reinitialise the Redraw Assistant.
+                if(leftLineList != null)
+                {
+                    redrawAss = new RedrawAssistant(leftLineList);
+                    UpdateSizes();
+                    overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, -1, false);
+                    RedrawRightImage();
+                }
             }
             UpdateButtonStatus();
+            UpdateSizes();
         }
 
-        //add a Point on every tick to the Drawpath
+        /// <summary>
+        /// Add a Point on every tick to the Drawpath.
+        /// Or detect lines for deletion on every tick
+        /// </summary>
         private void mouseTimer_Tick(object sender, EventArgs e)
         {
+            if(cursorPositions.Count > 0) { previousCursorPosition = cursorPositions.Dequeue(); }
+            else { previousCursorPosition = currentCursorPosition; }
             cursorPositions.Enqueue(currentCursorPosition);
-            previousCursorPosition = cursorPositions.Dequeue();
+            //Drawing
             if (currentState.Equals(ProgramState.Draw) && mousePressed)
             {
+                rightGraph = Graphics.FromImage(rightImage);
                 currentLine.Add(currentCursorPosition);
                 Line drawline = new Line(currentLine);
                 drawline.DrawLine(rightGraph);
                 pictureBoxRight.Image = rightImage;
+                //Redraw overlay gets ticked
+                overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, rightLineList.Count, false);
+                RedrawRightImage();
             }
+            //Deleting
             if (currentState.Equals(ProgramState.Delete) && mousePressed)
             {
-                List<Point> uncheckedPoints = Line.BresenhamLineAlgorithm(previousCursorPosition, currentCursorPosition);
+                List<Point> uncheckedPoints = GeometryCalculator.BresenhamLineAlgorithm(previousCursorPosition, currentCursorPosition);
                 foreach (Point currPoint in uncheckedPoints)
                 {
-                    HashSet<int> linesToDelete = CheckDeletionMatrixesAroundPoint(currPoint, deletionSize);
+                    HashSet<int> linesToDelete = CheckDeletionMatrixesAroundPoint(currPoint, deletionRadius);
                     if (linesToDelete.Count > 0)
                     {
                         historyOfActions.AddNewAction(new SketchAction(SketchAction.ActionType.Delete, linesToDelete));
@@ -343,6 +409,8 @@ namespace SketchAssistant
                             rightLineList[lineID] = new Tuple<bool, Line>(false, rightLineList[lineID].Item2);
                         }
                         RepopulateDeletionMatrixes();
+                        //Redraw overlay gets ticked
+                        overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, -1, false);
                         RedrawRightImage();
                     }
                 }
@@ -353,6 +421,29 @@ namespace SketchAssistant
         /*** HELPER FUNCTIONS START HERE ***/
         /***********************************/
 
+        /// <summary>
+        /// A function that returns a white canvas for a given width and height.
+        /// </summary>
+        /// <param name="width">The width of the canvas in pixels</param>
+        /// <param name="height">The height of the canvas in pixels</param>
+        /// <returns>The new canvas</returns>
+        private Image GetEmptyCanvas(int width, int height)
+        {
+            Image image;
+            try
+            {
+                image = new Bitmap(width, height);
+            }
+            catch(ArgumentException e)
+            {
+                ShowInfoMessage("The requested canvas size caused an error: \n" + e.ToString() + "\n The Canvas will be set to match your window.");
+                image = new Bitmap(pictureBoxLeft.Width, pictureBoxLeft.Height);
+            }
+            Graphics graph = Graphics.FromImage(image);
+            graph.FillRectangle(Brushes.White, 0, 0, width + 10, height + 10);
+            return image;
+        }
+
         /// <summary>
         /// Creates an empty Canvas
         /// </summary>
@@ -360,20 +451,12 @@ namespace SketchAssistant
         {
             if (leftImage == null)
             {
-                rightImage = new Bitmap(pictureBoxRight.Width, pictureBoxRight.Height);
-                rightGraph = Graphics.FromImage(rightImage);
-                rightGraph.FillRectangle(Brushes.White, 0, 0, pictureBoxRight.Width + 10, pictureBoxRight.Height + 10);
-                pictureBoxRight.Image = rightImage;
+                SetAndRefreshRightImage(GetEmptyCanvas(pictureBoxRight.Width, pictureBoxRight.Height));
             }
             else
             {
-                rightImage = new Bitmap(leftImage.Width, leftImage.Height);
-                rightGraph = Graphics.FromImage(rightImage);
-                rightGraph.FillRectangle(Brushes.White, 0, 0, leftImage.Width + 10, leftImage.Height + 10);
-                pictureBoxRight.Image = rightImage;
+                SetAndRefreshRightImage(GetEmptyCanvas(leftImage.Width, leftImage.Height));
             }
-            this.Refresh();
-            pictureBoxRight.Refresh();
         }
 
         /// <summary>
@@ -385,17 +468,12 @@ namespace SketchAssistant
         {
             if (width == 0)
             {
-                leftImage = new Bitmap(pictureBoxLeft.Width, pictureBoxLeft.Height);
+                SetAndRefreshLeftImage(GetEmptyCanvas(pictureBoxLeft.Width, pictureBoxLeft.Height));
             }
             else
             {
-                leftImage = new Bitmap(width, height);
+                SetAndRefreshLeftImage(GetEmptyCanvas(width, height));
             }
-            Graphics.FromImage(leftImage).FillRectangle(Brushes.White, 0, 0, pictureBoxLeft.Width + 10, pictureBoxLeft.Height + 10);
-            pictureBoxLeft.Image = leftImage;
-            
-            this.Refresh();
-            pictureBoxLeft.Refresh();
         }
 
         /// <summary>
@@ -403,17 +481,55 @@ namespace SketchAssistant
         /// </summary>
         private void RedrawRightImage()
         {
-            DrawEmptyCanvasRight();
+            var workingCanvas = GetEmptyCanvas(rightImage.Width, rightImage.Height);
+            var workingGraph = Graphics.FromImage(workingCanvas);
+            //Lines
             foreach (Tuple<bool, Line> lineBoolTuple in rightLineList)
             {
                 if (lineBoolTuple.Item1)
                 {
-                    lineBoolTuple.Item2.DrawLine(rightGraph);
+                    lineBoolTuple.Item2.DrawLine(workingGraph);
                 }
             }
+            //The Line being currently drawn
+            if(currentLine != null && currentLine.Count > 0 && currentState.Equals(ProgramState.Draw) && mousePressed)
+            {
+                var currLine = new Line(currentLine);
+                currLine.DrawLine(workingGraph);
+            }
+            //Overlay Items
+            foreach (HashSet<Point> item in overlayItems)
+            {
+                foreach(Point p in item)
+                {
+                    workingGraph.FillRectangle(Brushes.Green, p.X, p.Y, 1, 1);
+                }
+            }
+            SetAndRefreshRightImage(workingCanvas);
+        }
+
+        /// <summary>
+        /// A function to set rightImage and to refresh the respective PictureBox with it.
+        /// </summary>
+        /// <param name="image">The new Image</param>
+        private void SetAndRefreshRightImage(Image image)
+        {
+            rightImage = image;
+            pictureBoxRight.Image = rightImage;
             pictureBoxRight.Refresh();
         }
 
+        /// <summary>
+        /// A function to set leftImage and to refresh the respective PictureBox with it.
+        /// </summary>
+        /// <param name="image">The new Image</param>
+        private void SetAndRefreshLeftImage(Image image)
+        {
+            leftImage = image;
+            pictureBoxLeft.Image = leftImage;
+            pictureBoxLeft.Refresh();
+        }
+
         /// <summary>
         /// Change the status of whether or not the lines are shown.
         /// </summary>
@@ -547,27 +663,17 @@ namespace SketchAssistant
         /// <param name="p">The point around which to check.</param>
         /// <param name="range">The range around the point. If range is 0, only the point is checked.</param>
         /// <returns>A List of all lines.</returns>
-        private HashSet<int> CheckDeletionMatrixesAroundPoint(Point p, uint range)
+        private HashSet<int> CheckDeletionMatrixesAroundPoint(Point p, int range)
         {
             HashSet<int> returnSet = new HashSet<int>();
 
-            if (p.X >= 0 && p.Y >= 0 && p.X < rightImage.Width && p.Y < rightImage.Height)
-            {
-                if (isFilledMatrix[p.X, p.Y])
-                {
-                    returnSet.UnionWith(linesMatrix[p.X, p.Y]);
-                }
-            }
-            for (int x_mod = (int)range*(-1); x_mod < range; x_mod++)
+            foreach(Point pnt in GeometryCalculator.FilledCircleAlgorithm(p, (int)range))
             {
-                for (int y_mod = (int)range * (-1); y_mod < range; y_mod++)
+                if(pnt.X >= 0 && pnt.Y >= 0 && pnt.X < rightImage.Width && pnt.Y < rightImage.Height)
                 {
-                    if (p.X + x_mod >= 0 && p.Y + y_mod >= 0 && p.X + x_mod < rightImage.Width && p.Y + y_mod < rightImage.Height)
+                    if (isFilledMatrix[pnt.X, pnt.Y])
                     {
-                        if (isFilledMatrix[p.X + x_mod, p.Y + y_mod])
-                        {
-                            returnSet.UnionWith(linesMatrix[p.X + x_mod, p.Y + y_mod]);
-                        }
+                        returnSet.UnionWith(linesMatrix[pnt.X, pnt.Y]);
                     }
                 }
             }
@@ -596,14 +702,89 @@ namespace SketchAssistant
         {
             MessageBox.Show(message);
         }
+        
+        /// <summary>
+        /// Will calculate the start and endpoints of the given line on the right canvas.
+        /// </summary>
+        /// <param name="line">The line.</param>
+        /// <param name="size">The size of the circle with which the endpoints of the line are marked.</param>
+        private Tuple<HashSet<Point>, HashSet<Point>> CalculateStartAndEnd(Line line, int size)
+        {
+            var circle0 = GeometryCalculator.FilledCircleAlgorithm(line.GetStartPoint(), size);
+            var circle1 = GeometryCalculator.FilledCircleAlgorithm(line.GetEndPoint(), size);
+            var currentLineEndings = new Tuple<HashSet<Point>, HashSet<Point>>(circle0, circle1);
+            return currentLineEndings;
+        }
+
+        /// <summary>
+        /// A helper Function that updates the markerRadius & deletionRadius, considering the size of the canvas.
+        /// </summary>
+        private void UpdateSizes()
+        {
+            if (rightImage != null)
+            {
+                int widthImage = pictureBoxRight.Image.Width;
+                int heightImage = pictureBoxRight.Image.Height;
+                int widthBox = pictureBoxRight.Width;
+                int heightBox = pictureBoxRight.Height;
+                
+                float imageRatio = (float)widthImage / (float)heightImage;
+                float containerRatio = (float)widthBox / (float)heightBox;
+                float zoomFactor = 0;
+                if (imageRatio >= containerRatio)
+                {
+                    //Image is wider than it is high
+                    zoomFactor = (float)widthImage / (float)widthBox;
+                }
+                else
+                {
+                    //Image is higher than it is wide
+                    zoomFactor = (float)heightImage / (float)heightBox;
+                }
+                markerRadius = (int)(10 * zoomFactor);
+                redrawAss.SetMarkerRadius(markerRadius);
+                deletionRadius = (int)(5 * zoomFactor);
+            }
+        }
+
+        /// <summary>
+        /// Checks if there is unsaved progess, and warns the user. Returns True if it safe to continue.
+        /// </summary>
+        /// <returns>true if there is none, or the user wishes to continue without saving.
+        /// false if there is progress, and the user doesn't wish to continue.</returns>
+        private bool CheckSavedStatus()
+        {
+            if (!historyOfActions.IsEmpty())
+            {
+                return (MessageBox.Show("You have unsaved changes, do you wish to continue?",
+                    "Attention", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes);
+            }
+            return true;
+        }
+
+        /********************************************/
+        /*** TESTING RELATED FUNCTIONS START HERE ***/
+        /********************************************/
 
         /// <summary>
         /// returns all instance variables in the order of their definition for testing
         /// </summary>
-        /// <returns>all instance variables in the order of their definition</returns>
-        public Object[]/*(ProgramState, FileImporter, OpenFileDialog, Image, List<Line>, Image, List<Point>, List<Tuple<bool, Line>>, bool, Point, Point, Queue<Point>, Graphics, bool[,], HashSet<int>[,], uint, ActionHistory)*/ GetAllVariables()
+        /// <returns>A list of tuples containing names of variables and the variable themselves. 
+        /// Cast according to the Type definitions in the class variable section.</returns>
+        public List<Tuple<String, Object>>GetAllVariables()
         {
-            return new Object[] { currentState, fileImporter, openFileDialog, leftImage, leftLineList, rightImage, currentLine, rightLineList, mousePressed, currentCursorPosition, previousCursorPosition, cursorPositions, rightGraph, isFilledMatrix, linesMatrix, deletionSize, historyOfActions };
+            var objArr = new (String, object)[] { ("currentState", currentState), ("fileImporter", fileImporter), ("openFileDialog", openFileDialog),
+                ("leftImage", leftImage), ("leftLineList", leftLineList), ("rightImage", rightImage), ("currentLine", currentLine),
+                ("rightLineList", rightLineList), ("mousePressed", mousePressed), ("currentCursorPosition", currentCursorPosition),
+                ("previousCursorPosition", previousCursorPosition), ("cursorPositions", cursorPositions), ("rightGraph", rightGraph),
+                ("isFilledMatrix", isFilledMatrix), ("linesMatrix", linesMatrix), ("deletionRadius", deletionRadius),
+                ("historyOfActions", historyOfActions), ("overlayItems", overlayItems), ("redrawAss", redrawAss), ("markerRadius", markerRadius) };
+            var varArr = new List<Tuple<String, Object>>();
+            foreach((String, object) obj in objArr)
+            {
+                varArr.Add(new Tuple<string, object>(obj.Item1, obj.Item2));
+            }
+            return varArr;
         }
 
         /// <summary>
@@ -617,7 +798,5 @@ namespace SketchAssistant
             DrawEmptyCanvasLeft(width, height);
             BindAndDrawLeftImage(newImage);
         }
-
-
     }
 }

+ 204 - 0
SketchAssistant/SketchAssistant/GeometryCalculator.cs

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

+ 2 - 116
SketchAssistant/SketchAssistant/Line.cs

@@ -75,10 +75,9 @@ namespace SketchAssistant
         /// <returns>The given Graphics element with the additional line</returns>
         public Graphics DrawLine(Graphics canvas)
         {
-            Pen thePen = new Pen(Color.Black);
             for(int i = 0; i < linePoints.Count - 1 ; i++)
             {
-                canvas.DrawLine(thePen, linePoints[i], linePoints[i + 1]);
+                canvas.DrawLine(Pens.Black, linePoints[i], linePoints[i + 1]);
             }
             //If there is only one point
             if(linePoints.Count == 1){ canvas.FillRectangle(Brushes.Black, linePoints[0].X, linePoints[0].Y, 1, 1); }
@@ -126,7 +125,7 @@ namespace SketchAssistant
                 for (int i = 0; i < linePoints.Count - 1; i++)
                 {
                     nullValue += linePoints[i + 1].X;
-                    List<Point> partialList = BresenhamLineAlgorithm(linePoints[i], linePoints[i + 1]);
+                    List<Point> partialList = GeometryCalculator.BresenhamLineAlgorithm(linePoints[i], linePoints[i + 1]);
                     tempList.AddRange(partialList);
                 }
                 Point nullPoint = new Point(nullValue, 0);
@@ -149,118 +148,5 @@ namespace SketchAssistant
                 linePoints = new List<Point>(newList);
             }
         }
-
-        /// <summary>
-        /// An implementation of the Bresenham Line Algorithm, 
-        /// which calculates all points between two points in a straight line.
-        /// Implemented using the pseudocode on Wikipedia.
-        /// </summary>
-        /// <param name="p0">The start point</param>
-        /// <param name="p1">The end point</param>
-        /// <returns>All points between p0 and p1 (including p0 and p1)</returns>
-        public static List<Point> BresenhamLineAlgorithm(Point p0, Point p1)
-        {
-            int deltaX = p1.X - p0.X;
-            int deltaY = p1.Y - p0.Y;
-            List<Point> returnList;
-
-            if (Math.Abs(deltaY) < Math.Abs(deltaX))
-            {
-                if(p0.X > p1.X)
-                {
-                    returnList = GetLineLow(p1.X, p1.Y, p0.X, p0.Y);
-                    returnList.Reverse();
-                }
-                else
-                {
-                    returnList = GetLineLow(p0.X, p0.Y, p1.X, p1.Y);
-                }
-            }
-            else
-            {
-                if (p0.Y > p1.Y)
-                {
-                    returnList = GetLineHigh(p1.X, p1.Y, p0.X, p0.Y);
-                    returnList.Reverse();
-                }
-                else
-                {
-                    returnList = GetLineHigh(p0.X, p0.Y, p1.X, p1.Y);
-                }
-            }
-            return returnList;
-        }
-
-        /// <summary>
-        /// Helping function of the Bresenham Line algorithm,
-        /// under the assumption that abs(deltaY) is smaller than abs(deltX)
-        /// and x0 is smaller than x1
-        /// </summary>
-        /// <param name="x0">x value of point 0</param>
-        /// <param name="y0">y value of point 0</param>
-        /// <param name="x1">x value of point 1</param>
-        /// <param name="y1">y value of point 1</param>
-        /// <returns>All points on the line between the two points</returns>
-        private static List<Point> GetLineLow(int x0, int y0, int x1, int y1)
-        {
-            List<Point> returnList = new List<Point>();
-            int dx = x1 - x0;
-            int dy = y1 - y0;
-            int yi = 1;
-            if(dy < 0)
-            {
-                yi = -1;
-                dy = -dy;
-            }
-            int D = 2 * dy - dx;
-            int y = y0;
-            for (int x = x0; x <= x1; x++)
-            {
-                returnList.Add(new Point(x, y));
-                if (D > 0)
-                {
-                    y = y + yi;
-                    D = D - 2 * dx;
-                }
-                D = D + 2 * dy;
-            }
-            return returnList;
-        }
-
-        /// <summary>
-        /// Helping function of the Bresenham Line algorithm,
-        /// under the assumption that abs(deltaY) is larger or equal than abs(deltX)
-        /// and y0 is smaller than y1
-        /// </summary>
-        /// <param name="x0">x value of point 0</param>
-        /// <param name="y0">y value of point 0</param>
-        /// <param name="x1">x value of point 1</param>
-        /// <param name="y1">y value of point 1</param>
-        /// <returns>All points on the line between the two points</returns>
-        private static List<Point> GetLineHigh(int x0, int y0, int x1, int y1)
-        {
-            List<Point> returnList = new List<Point>();
-            int dx = x1 - x0;
-            int dy = y1 - y0;
-            int xi = 1;
-            if (dx < 0)
-            {
-                xi = -1;
-                dx = -dx;
-            }
-            int D = 2 * dx - dy;
-            int x = x0;
-            for (int y = y0; y <= y1; y++)
-            {
-                returnList.Add(new Point(x, y));
-                if (D > 0)
-                {
-                    x = x + xi;
-                    D = D - 2 * dy;
-                }
-                D = D + 2 * dx;
-            }
-            return returnList;
-        }
     }
 }

+ 221 - 0
SketchAssistant/SketchAssistant/RedrawAssistant.cs

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

+ 2 - 0
SketchAssistant/SketchAssistant/SketchAssistant.csproj

@@ -69,8 +69,10 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="RedrawAssistant.cs" />
     <Compile Include="FileImporter.cs" />
     <Compile Include="FileImporterException.cs" />
+    <Compile Include="GeometryCalculator.cs" />
     <Compile Include="SketchAction.cs" />
     <Compile Include="ActionHistory.cs" />
     <Compile Include="Line.cs" />