Browse Source

started userstory 16

Martin Edlund 5 years ago
parent
commit
1b50a44c23

+ 6 - 0
SketchAssistant/SketchAssistant.sln

@@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SketchAssistant", "SketchAs
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SketchAssistant.Tests", "SketchAssistant.Tests\SketchAssistant.Tests.csproj", "{7DCDC31A-8291-4B05-93D6-DCC5DE27A4A0}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SketchAssistantWPF", "SketchAssistantWPF\SketchAssistantWPF.csproj", "{EE53AE79-2AA0-4F43-9638-1789B189D5C3}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -21,6 +23,10 @@ Global
 		{7DCDC31A-8291-4B05-93D6-DCC5DE27A4A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{7DCDC31A-8291-4B05-93D6-DCC5DE27A4A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{7DCDC31A-8291-4B05-93D6-DCC5DE27A4A0}.Release|Any CPU.Build.0 = Release|Any CPU
+		{EE53AE79-2AA0-4F43-9638-1789B189D5C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{EE53AE79-2AA0-4F43-9638-1789B189D5C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{EE53AE79-2AA0-4F43-9638-1789B189D5C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{EE53AE79-2AA0-4F43-9638-1789B189D5C3}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 106 - 0
SketchAssistant/SketchAssistantWPF/ActionHistory.cs

@@ -0,0 +1,106 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SketchAssistantWPF
+{
+    public class ActionHistory
+    {
+        //History of Actions taken
+        List<SketchAction> actionHistory;
+        //The current position in the actionHistory
+        Tuple<int, SketchAction> currentAction;
+
+        public ActionHistory()
+        {
+            actionHistory = new List<SketchAction>();
+            currentAction = new Tuple<int, SketchAction>(-1, null);
+            AddNewAction(new SketchAction(SketchAction.ActionType.Start, -1));
+        }
+
+        /// <summary>
+        /// Adds a new action to the action history.
+        /// </summary>
+        /// <param name="newAction">The newly added action.</param>
+        /// <returns>The message to be displayed</returns>
+        public String AddNewAction(SketchAction newAction)
+        {
+            //The current Action is before the last action taken, delete everything after the current action.
+            if (currentAction.Item1 < actionHistory.Count - 1)
+            {
+                actionHistory.RemoveRange(currentAction.Item1 + 1, actionHistory.Count - (currentAction.Item1 + 1));
+            }
+            actionHistory.Add(newAction);
+            currentAction = new Tuple<int, SketchAction>(actionHistory.Count - 1, newAction);
+            return UpdateStatusLabel();
+        }
+
+        /// <summary>
+        /// Changes the currentAction.
+        /// </summary>
+        /// <param name="moveBack">If True, moves the current action back one slot, if False, moves it forward.</param>
+        /// <returns>The message to be displayed</returns>
+        public String MoveAction(bool moveBack)
+        {
+            if (moveBack && CanUndo())
+            {
+                currentAction = new Tuple<int, SketchAction>(currentAction.Item1 - 1, actionHistory[currentAction.Item1 - 1]);
+            }
+            if (!moveBack && CanRedo())
+            {
+                currentAction = new Tuple<int, SketchAction>(currentAction.Item1 + 1, actionHistory[currentAction.Item1 + 1]);
+            }
+            return UpdateStatusLabel();
+        }
+
+        /// <summary>
+        /// Returns the current action.
+        /// </summary>
+        /// <returns>The current action.</returns>
+        public SketchAction GetCurrentAction()
+        {
+            return currentAction.Item2;
+        }
+
+        /// <summary>
+        /// Return whether or not an action can be undone.
+        /// </summary>
+        /// <returns>True if an action can be undone.</returns>
+        public bool CanUndo()
+        {
+            if (currentAction.Item1 > 0) { return true; }
+            else { return false; }
+        }
+
+        /// <summary>
+        /// Return whether or not an action can be redone.
+        /// </summary>
+        /// <returns>True if an action can be redone.</returns>
+        public bool CanRedo()
+        {
+            if (currentAction.Item1 < actionHistory.Count - 1) { return true; }
+            else { return false; }
+        }
+
+        /// <summary>
+        /// Returns whether or not the history is empty.
+        /// </summary>
+        /// <returns>true if the history is empty, otherwise false</returns>
+        public bool IsEmpty()
+        {
+            if (actionHistory.Count == 1) { return true; }
+            else { return false; }
+        }
+
+        /// <summary>
+        /// Updates the status label if there is one given.
+        /// </summary>
+        /// <returns>The message to be displayed</returns>
+        private String UpdateStatusLabel()
+        {
+            return "Last Action: " + currentAction.Item2.GetActionInformation();
+        }
+    }
+}

+ 6 - 0
SketchAssistant/SketchAssistantWPF/App.config

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

+ 9 - 0
SketchAssistant/SketchAssistantWPF/App.xaml

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

+ 17 - 0
SketchAssistant/SketchAssistantWPF/App.xaml.cs

@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace SketchAssistantWPF
+{
+    /// <summary>
+    /// Interaction logic for App.xaml
+    /// </summary>
+    public partial class App : Application
+    {
+    }
+}

+ 144 - 0
SketchAssistant/SketchAssistantWPF/FileImporter.cs

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

+ 26 - 0
SketchAssistant/SketchAssistantWPF/FileImporterException.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SketchAssistantWPF
+{
+    public class FileImporterException : Exception
+    {
+        /// <summary>
+        /// the clean and formatted message to show to the user
+        /// </summary>
+        String showMessage;
+
+        public FileImporterException(String message, String hint, int lineNumber) : base(message)
+        {
+            showMessage = "Could not import file:\n\n" + message + (hint == null ? "" : "\n(Hint: " + hint + ")") + (lineNumber == -1 ? "" : "\n\n-line: " + lineNumber);
+        }
+
+        public override string ToString()
+        {
+            return showMessage;
+        }
+    }
+}

+ 204 - 0
SketchAssistant/SketchAssistantWPF/GeometryCalculator.cs

@@ -0,0 +1,204 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace SketchAssistantWPF
+{
+    /// <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;
+        }
+    }
+}

+ 152 - 0
SketchAssistant/SketchAssistantWPF/Line.cs

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

+ 573 - 0
SketchAssistant/SketchAssistantWPF/MVP_Model.cs

@@ -0,0 +1,573 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace SketchAssistantWPF
+{
+    public class MVP_Model
+    {
+        /// <summary>
+        /// The Presenter of the MVP-Model.
+        /// </summary>
+        MVP_Presenter programPresenter;
+        /// <summary>
+        /// History of Actions
+        /// </summary>
+        ActionHistory historyOfActions;
+        /// <summary>
+        /// The assistant responsible for the redraw mode
+        /// </summary>
+        //RedrawAssistant redrawAss;
+
+        /*******************/
+        /*** ENUMERATORS ***/
+        /*******************/
+
+        /***********************/
+        /*** CLASS VARIABLES ***/
+        /***********************/
+
+        /// <summary>
+        /// If the program is in drawing mode.
+        /// </summary>
+        bool inDrawingMode;
+        /// <summary>
+        /// If the mouse is currently pressed or not.
+        /// </summary>
+        bool mousePressed;
+        /// <summary>
+        /// Size of deletion area
+        /// </summary>
+        int deletionRadius = 5;
+        /// <summary>
+        /// Size of areas marking endpoints of lines in the redraw mode.
+        /// </summary>
+        int markerRadius = 10;
+        /// <summary>
+        /// The Position of the Cursor in the right picture box
+        /// </summary>
+        Point currentCursorPosition;
+        /// <summary>
+        /// The Previous Cursor Position in the right picture box
+        /// </summary>
+        Point previousCursorPosition;
+        /// <summary>
+        /// Queue for the cursorPositions
+        /// </summary>
+        Queue<Point> cursorPositions = new Queue<Point>();
+        /// <summary>
+        /// Lookup Matrix for checking postions of lines in the image
+        /// </summary>
+        bool[,] isFilledMatrix;
+        /// <summary>
+        /// Lookup Matrix for getting line ids at a certain postions of the image
+        /// </summary>
+        HashSet<int>[,] linesMatrix;
+        /// <summary>
+        /// List of items which will be overlayed over the right canvas.
+        /// </summary>
+        List<Tuple<bool, HashSet<Point>>> overlayItems;
+        /// <summary>
+        /// Width of the LeftImageBox.
+        /// </summary>
+        public int leftImageBoxWidth;
+        /// <summary>
+        /// Height of the LeftImageBox.
+        /// </summary>
+        public int leftImageBoxHeight;
+        /// <summary>
+        /// Width of the RightImageBox.
+        /// </summary>
+        public int rightImageBoxWidth;
+        /// <summary>
+        /// Height of the RightImageBox.
+        /// </summary>
+        public int rightImageBoxHeight;
+
+        //Images
+        Image leftImage;
+
+        List<Line> leftLineList;
+
+        Image rightImageWithoutOverlay;
+
+        Image rightImageWithOverlay;
+
+        List<Tuple<bool, Line>> rightLineList;
+
+        List<Point> currentLine;
+
+
+
+        public MVP_Model(MVP_Presenter presenter)
+        {
+            programPresenter = presenter;
+            historyOfActions = new ActionHistory();
+            //redrawAss = new RedrawAssistant();
+            rightLineList = new List<Tuple<bool, Line>>();
+            overlayItems = new List<Tuple<bool, HashSet<Point>>>();
+        }
+
+        /**************************/
+        /*** INTERNAL FUNCTIONS ***/
+        /**************************/
+
+        /// <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;
+            WriteableBitmap newCanvas;
+            try
+            {
+                newCanvas = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgra32, null);
+                image = new Bitmap(width, height);
+            }
+            catch (ArgumentException e)
+            {
+                programPresenter.PassMessageToView("The requested canvas size caused an error: \n"
+                    + e.ToString() + "\n The Canvas will be set to match your window.");
+                newCanvas = new WriteableBitmap(leftImageBoxWidth, leftImageBoxHeight, 96, 96, PixelFormats.Bgra32, null);
+            }
+            Graphics graph = Graphics.FromImage(image);
+            graph.FillRectangle(Brushes.White, 0, 0, width + 10, height + 10);
+            return image;
+        }
+
+
+        /// <summary>
+        /// Creates an empty Canvas on the left
+        /// </summary>
+        /// <param name="width"> width of the new canvas in pixels </param>
+        /// <param name="height"> height of the new canvas in pixels </param>
+        private void DrawEmptyCanvasLeft(int width, int height)
+        {
+            if (width == 0)
+            {
+                leftImage = GetEmptyCanvas(leftImageBoxWidth, leftImageBoxHeight);
+            }
+            else
+            {
+                leftImage = GetEmptyCanvas(width, height);
+            }
+            programPresenter.UpdateLeftImage(leftImage);
+        }
+
+        /// <summary>
+        /// Redraws all lines in rightLineList, for which their associated boolean value equals true and calls RedrawRightOverlay.
+        /// </summary>
+        private void RedrawRightImage()
+        {
+            var workingCanvas = GetEmptyCanvas(rightImageWithoutOverlay.Width, rightImageWithoutOverlay.Height);
+            var workingGraph = Graphics.FromImage(workingCanvas);
+            //Lines
+            foreach (Tuple<bool, Line> lineBoolTuple in rightLineList)
+            {
+                if (lineBoolTuple.Item1)
+                {
+                    lineBoolTuple.Item2.DrawLine(workingGraph);
+                }
+            }
+            //The Line being currently drawn
+            if (currentLine != null && currentLine.Count > 0 && inDrawingMode && mousePressed)
+            {
+                var currLine = new Line(currentLine);
+                currLine.DrawLine(workingGraph);
+            }
+            rightImageWithoutOverlay = workingCanvas;
+            //Redraw the Overlay if needed
+            if (leftImage != null)
+            {
+                RedrawRightOverlay();
+            }
+            else
+            {
+                programPresenter.UpdateRightImage(rightImageWithoutOverlay);
+            }
+        }
+
+        /// <summary>
+        /// Redraws all elements in the overlay items for which the respective boolean value is true.
+        /// </summary>
+        private void RedrawRightOverlay()
+        {
+            var workingCanvas = rightImageWithoutOverlay;
+            var workingGraph = Graphics.FromImage(workingCanvas);
+            foreach (Tuple<bool, HashSet<Point>> tup in overlayItems)
+            {
+                if (tup.Item1)
+                {
+                    foreach (Point p in tup.Item2)
+                    {
+                        workingGraph.FillRectangle(Brushes.Green, p.X, p.Y, 1, 1);
+                    }
+                }
+            }
+            rightImageWithOverlay = workingCanvas;
+            programPresenter.UpdateRightImage(rightImageWithOverlay);
+        }
+
+        /// <summary>
+        /// Change the status of whether or not the lines are shown.
+        /// </summary>
+        /// <param name="lines">The HashSet containing the affected Line IDs.</param>
+        /// <param name="shown">True if the lines should be shown, false if they should be hidden.</param>
+        private void ChangeLines(HashSet<int> lines, bool shown)
+        {
+            var changed = false;
+            foreach (int lineId in lines)
+            {
+                if (lineId <= rightLineList.Count - 1 && lineId >= 0)
+                {
+                    rightLineList[lineId] = new Tuple<bool, Line>(shown, rightLineList[lineId].Item2);
+                    changed = true;
+                }
+            }
+            if (changed) { RedrawRightImage(); }
+        }
+
+        /// <summary>
+        /// A function that populates the matrixes needed for deletion detection with line data.
+        /// </summary>
+        private void RepopulateDeletionMatrixes()
+        {
+            if (rightImageWithoutOverlay != null)
+            {
+                isFilledMatrix = new bool[rightImageWithoutOverlay.Width, rightImageWithoutOverlay.Height];
+                linesMatrix = new HashSet<int>[rightImageWithoutOverlay.Width, rightImageWithoutOverlay.Height];
+                foreach (Tuple<bool, Line> lineTuple in rightLineList)
+                {
+                    if (lineTuple.Item1)
+                    {
+                        lineTuple.Item2.PopulateMatrixes(isFilledMatrix, linesMatrix);
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// A function that checks the deletion matrixes at a certain point 
+        /// and returns all Line ids at that point and in a square around it in a certain range.
+        /// </summary>
+        /// <param name="p">The point around which to check.</param>
+        /// <param name="range">The range around the point. If range is 0, only the point is checked.</param>
+        /// <returns>A List of all lines.</returns>
+        private HashSet<int> CheckDeletionMatrixesAroundPoint(Point p, int range)
+        {
+            HashSet<int> returnSet = new HashSet<int>();
+
+            foreach (Point pnt in GeometryCalculator.FilledCircleAlgorithm(p, (int)range))
+            {
+                if (pnt.X >= 0 && pnt.Y >= 0 && pnt.X < rightImageWithoutOverlay.Width && pnt.Y < rightImageWithoutOverlay.Height)
+                {
+                    if (isFilledMatrix[pnt.X, pnt.Y])
+                    {
+                        returnSet.UnionWith(linesMatrix[pnt.X, pnt.Y]);
+                    }
+                }
+            }
+            return returnSet;
+        }
+
+        /*
+        /// <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>
+        /// Tells the Presenter to Update the UI
+        /// </summary>
+        private void UpdateUI()
+        {
+            programPresenter.UpdateUIState(inDrawingMode, historyOfActions.CanUndo(), historyOfActions.CanRedo(), (rightImageWithoutOverlay != null));
+        }
+
+
+        /********************************************/
+        /*** FUNCTIONS TO INTERACT WITH PRESENTER ***/
+        /********************************************/
+
+        /// <summary>
+        /// Creates an empty Canvas
+        /// </summary>
+        public void DrawEmptyCanvasRight()
+        {
+            if (leftImage == null)
+            {
+                rightImageWithoutOverlay = GetEmptyCanvas(leftImageBoxWidth, leftImageBoxHeight);
+            }
+            else
+            {
+                rightImageWithoutOverlay = GetEmptyCanvas(leftImage.Width, leftImage.Height);
+            }
+            RepopulateDeletionMatrixes();
+            rightImageWithOverlay = rightImageWithoutOverlay;
+            programPresenter.UpdateRightImage(rightImageWithOverlay);
+        }
+
+        /// <summary>
+        /// The function to set the left image.
+        /// </summary>
+        /// <param name="width">The width of the left image.</param>
+        /// <param name="height">The height of the left image.</param>
+        /// <param name="listOfLines">The List of Lines to be displayed in the left image.</param>
+        public void SetLeftLineList(int width, int height, List<Line> listOfLines)
+        {
+            var workingCanvas = GetEmptyCanvas(width, height);
+            var workingGraph = Graphics.FromImage(workingCanvas);
+            leftLineList = listOfLines;
+            //redrawAss = new RedrawAssistant(leftLineList);
+            //overlayItems = redrawAss.Initialize(markerRadius);
+            //Lines
+            foreach (Line line in leftLineList)
+            {
+                line.DrawLine(workingGraph);
+            }
+            leftImage = workingCanvas;
+            programPresenter.UpdateLeftImage(leftImage);
+            //Set right image to same size as left image and delete linelist
+            DrawEmptyCanvasRight();
+            rightLineList = new List<Tuple<bool, Line>>();
+        }
+
+        /// <summary>
+        /// Will undo the last action taken, if the action history allows it.
+        /// </summary>
+        public void Undo()
+        {
+            if (historyOfActions.CanUndo())
+            {
+                HashSet<int> affectedLines = historyOfActions.GetCurrentAction().GetLineIDs();
+                SketchAction.ActionType undoAction = historyOfActions.GetCurrentAction().GetActionType();
+                switch (undoAction)
+                {
+                    case SketchAction.ActionType.Delete:
+                        //Deleted Lines need to be shown
+                        ChangeLines(affectedLines, true);
+                        break;
+                    case SketchAction.ActionType.Draw:
+                        //Drawn lines need to be hidden
+                        ChangeLines(affectedLines, false);
+                        break;
+                    default:
+                        break;
+                }
+                if (leftImage != null)
+                {
+                    //overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, -1, false);
+                }
+                RedrawRightImage();
+            }
+            RepopulateDeletionMatrixes();
+            programPresenter.PassLastActionTaken(historyOfActions.MoveAction(true));
+            UpdateUI();
+        }
+
+        /// <summary>
+        /// Will redo the last action undone, if the action history allows it.
+        /// </summary>
+        public void Redo()
+        {
+            if (historyOfActions.CanRedo())
+            {
+                programPresenter.PassLastActionTaken(historyOfActions.MoveAction(false));
+                HashSet<int> affectedLines = historyOfActions.GetCurrentAction().GetLineIDs();
+                SketchAction.ActionType redoAction = historyOfActions.GetCurrentAction().GetActionType();
+                switch (redoAction)
+                {
+                    case SketchAction.ActionType.Delete:
+                        //Deleted Lines need to be redeleted
+                        ChangeLines(affectedLines, false);
+                        break;
+                    case SketchAction.ActionType.Draw:
+                        //Drawn lines need to be redrawn
+                        ChangeLines(affectedLines, true);
+                        break;
+                    default:
+                        break;
+                }
+                if (leftImage != null)
+                {
+                    //overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, -1, false);
+                }
+                RedrawRightImage();
+                RepopulateDeletionMatrixes();
+            }
+            UpdateUI();
+        }
+
+        /// <summary>
+        /// The function called by the Presenter to change the drawing state of the program.
+        /// </summary>
+        /// <param name="nowDrawing">The new drawingstate of the program</param>
+        public void ChangeState(bool nowDrawing)
+        {
+            inDrawingMode = nowDrawing;
+            UpdateUI();
+        }
+
+        /// <summary>
+        /// A method to get the dimensions of the right image.
+        /// </summary>
+        /// <returns>A tuple containing the width and height of the right image.</returns>
+        public Tuple<int, int> GetRightImageDimensions()
+        {
+            if (rightImageWithoutOverlay != null)
+            {
+                return new Tuple<int, int>(rightImageWithoutOverlay.Width, rightImageWithoutOverlay.Height);
+            }
+            else
+            {
+                return new Tuple<int, int>(0, 0);
+            }
+        }
+
+        /// <summary>
+        /// Updates the current cursor position of the model.
+        /// </summary>
+        /// <param name="p">The new cursor position</param>
+        public void SetCurrentCursorPosition(Point p)
+        {
+            currentCursorPosition = p;
+        }
+
+        /// <summary>
+        /// Start a new Line, when the Mouse is pressed down.
+        /// </summary>
+        public void MouseDown()
+        {
+            mousePressed = true;
+            if (inDrawingMode)
+            {
+                currentLine = new List<Point>();
+            }
+        }
+
+        /// <summary>
+        /// Finish the current Line, when the pressed Mouse is released.
+        /// </summary>
+        public void MouseUp()
+        {
+            mousePressed = false;
+            if (inDrawingMode && currentLine.Count > 0)
+            {
+                Line newLine = new Line(currentLine, rightLineList.Count);
+                rightLineList.Add(new Tuple<bool, Line>(true, newLine));
+                newLine.PopulateMatrixes(isFilledMatrix, linesMatrix);
+                programPresenter.PassLastActionTaken(historyOfActions.AddNewAction(new SketchAction(SketchAction.ActionType.Draw, newLine.GetID())));
+                if (leftImage != null)
+                {
+                    //Execute a RedrawAssistant tick with the currently finished Line
+                    //overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, newLine.GetID(), true);
+                }
+                RedrawRightImage();
+            }
+            UpdateUI();
+        }
+
+        /// <summary>
+        /// Method to be called every tick. Updates the current Line, or checks for Lines to delete, depending on the drawing mode.
+        /// </summary>
+        public void Tick()
+        {
+            if (cursorPositions.Count > 0) { previousCursorPosition = cursorPositions.Dequeue(); }
+            else { previousCursorPosition = currentCursorPosition; }
+            cursorPositions.Enqueue(currentCursorPosition);
+            //Drawing
+            if (inDrawingMode && mousePressed)
+            {
+                var rightGraph = Graphics.FromImage(rightImageWithoutOverlay);
+                currentLine.Add(currentCursorPosition);
+                Line drawline = new Line(currentLine);
+                drawline.DrawLine(rightGraph);
+                RedrawRightOverlay();
+            }
+            //Deleting
+            if (!inDrawingMode && mousePressed)
+            {
+                List<Point> uncheckedPoints = GeometryCalculator.BresenhamLineAlgorithm(previousCursorPosition, currentCursorPosition);
+                foreach (Point currPoint in uncheckedPoints)
+                {
+                    HashSet<int> linesToDelete = CheckDeletionMatrixesAroundPoint(currPoint, deletionRadius);
+                    if (linesToDelete.Count > 0)
+                    {
+                        programPresenter.PassLastActionTaken(historyOfActions.AddNewAction(new SketchAction(SketchAction.ActionType.Delete, linesToDelete)));
+                        foreach (int lineID in linesToDelete)
+                        {
+                            rightLineList[lineID] = new Tuple<bool, Line>(false, rightLineList[lineID].Item2);
+                        }
+                        RepopulateDeletionMatrixes();
+                        if (leftImage != null)
+                        {
+                            //Redraw overlay gets ticked
+                            //overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, -1, false);
+                        }
+                        RedrawRightImage();
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// A helper Function that updates the markerRadius & deletionRadius, considering the size of the canvas.
+        /// </summary>
+        public void UpdateSizes()
+        {
+            if (rightImageWithoutOverlay != null)
+            {
+                int widthImage = rightImageWithoutOverlay.Width;
+                int heightImage = rightImageWithoutOverlay.Height;
+                int widthBox = rightImageBoxWidth;
+                int heightBox = rightImageBoxHeight;
+
+                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>
+        /// If there is unsaved progress.
+        /// </summary>
+        /// <returns>True if there is progress that has not been saved.</returns>
+        public bool HasUnsavedProgress()
+        {
+            return !historyOfActions.IsEmpty();
+        }
+    }
+}

+ 311 - 0
SketchAssistant/SketchAssistantWPF/MVP_Presenter.cs

@@ -0,0 +1,311 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+
+namespace SketchAssistantWPF
+{
+    public class MVP_Presenter
+    {
+        /// <summary>
+        /// The View of the MVP-Model, in this case Form1.
+        /// </summary>
+        MVP_View programView;
+        /// <summary>
+        /// The Model of the MVP-Model.
+        /// </summary>
+        MVP_Model programModel;
+
+        /*******************/
+        /*** ENUMERATORS ***/
+        /*******************/
+
+        public enum MouseAction
+        {
+            Click,
+            Down,
+            Up,
+            Move
+        }
+
+        /***********************/
+        /*** CLASS VARIABLES ***/
+        /***********************/
+
+        /// <summary>
+        /// Instance of FileImporter to handle drawing imports.
+        /// </summary>
+        private FileImporter fileImporter;
+
+
+        public MVP_Presenter(MVP_View form)
+        {
+            programView = form;
+            programModel = new MVP_Model(this);
+            //Initialize Class Variables
+            fileImporter = new FileImporter();
+        }
+
+        /***********************************/
+        /*** FUNCTIONS VIEW -> PRESENTER ***/
+        /***********************************/
+
+        /// <summary>
+        /// Pass-trough function to update the appropriate information of the model, when the window is resized.
+        /// </summary>
+        /// <param name="leftPBS">The new size of the left picture box.</param>
+        /// <param name="rightPBS">The new size of the left picture box.</param>
+        public void Resize(Tuple<int, int> leftPBS, Tuple<int, int> rightPBS)
+        {
+            programModel.leftImageBoxWidth = leftPBS.Item1;
+            programModel.leftImageBoxHeight = leftPBS.Item2;
+            programModel.rightImageBoxWidth = rightPBS.Item1;
+            programModel.rightImageBoxHeight = rightPBS.Item2;
+            programModel.UpdateSizes();
+        }
+
+        /// <summary>
+        /// Display a new FileDialog to load a collection of lines.
+        /// </summary>
+        public void ExamplePictureToolStripMenuItemClick()
+        {
+            var okToContinue = true;
+            if (programModel.HasUnsavedProgress())
+            {
+                okToContinue = programView.ShowWarning("You have unsaved progress. Continue?");
+            }
+            if (okToContinue)
+            {
+                var fileNameTup = programView.openNewDialog("Interactive Sketch-Assistant Drawing|*.isad");
+                if (!fileNameTup.Item1.Equals("") && !fileNameTup.Item2.Equals(""))
+                {
+                    programView.SetToolStripLoadStatus(fileNameTup.Item2);
+                    Tuple<int, int, List<Line>> values = fileImporter.ParseISADInputFile(fileNameTup.Item1);
+                    programModel.SetLeftLineList(values.Item1, values.Item2, values.Item3);
+                    programModel.ChangeState(true);
+                    programView.EnableTimer();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Pass-trough function to change the drawing state of the model.
+        /// </summary>
+        /// <param name="NowDrawing">Indicates if the program is in drawing (true) or deletion (false) mode.</param>
+        public void ChangeState(bool NowDrawing)
+        {
+            programModel.ChangeState(NowDrawing);
+        }
+
+        /// <summary>
+        /// Pass-trough function to undo an action.
+        /// </summary>
+        public void Undo()
+        {
+            programModel.Undo();
+        }
+
+        /// <summary>
+        /// Pass-trough function to redo an action.
+        /// </summary>
+        public void Redo()
+        {
+            programModel.Redo();
+        }
+
+        /// <summary>
+        /// Checks if there is unsaved progress, and promts the model to generate a new canvas if not.
+        /// </summary>
+        public void NewCanvas()
+        {
+            var okToContinue = true;
+            if (programModel.HasUnsavedProgress())
+            {
+                okToContinue = programView.ShowWarning("You have unsaved progress. Continue?");
+            }
+            if (okToContinue)
+            {
+                programModel.DrawEmptyCanvasRight();
+                programModel.ChangeState(true);
+                programView.EnableTimer();
+            }
+        }
+
+        /// <summary>
+        /// Pass-trough function for ticking the model.
+        /// </summary>
+        public void Tick()
+        {
+            programModel.Tick();
+        }
+
+        /// <summary>
+        /// Pass-trough when the mouse is moved.
+        /// </summary>
+        /// <param name="mouseAction">The action which is sent by the View.</param>
+        /// <param name="e">The Mouse event arguments.</param>
+        public void MouseEvent(MouseAction mouseAction, MouseEventArgs e)
+        {
+            switch (mouseAction)
+            {
+                case MouseAction.Move:
+                    programModel.SetCurrentCursorPosition(ConvertCoordinates(e.GetPosition());
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// Pass-trough function that calls the correct Mouse event of the model, when the mouse is clicked.
+        /// </summary>
+        /// <param name="mouseAction">The action which is sent by the View.</param>
+        /// <param name="e">The Mouse event arguments.</param>
+        public void MouseEvent(MouseAction mouseAction)
+        {
+            switch (mouseAction)
+            {
+                case MouseAction.Click:
+                    programModel.MouseDown();
+                    programModel.Tick();
+                    programModel.MouseUp();
+                    break;
+                case MouseAction.Down:
+                    programModel.MouseDown();
+                    break;
+                case MouseAction.Up:
+                    programModel.MouseUp();
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        /************************************/
+        /*** FUNCTIONS MODEL -> PRESENTER ***/
+        /************************************/
+
+        /// <summary>
+        /// Called by the model when the state of the Program changes. 
+        /// Changes the look of the UI according to the current state of the model.
+        /// </summary>
+        /// <param name="inDrawingMode">If the model is in Drawing Mode</param>
+        /// <param name="canUndo">If actions in the model can be undone</param>
+        /// <param name="canRedo">If actions in the model can be redone</param>
+        /// <param name="imageLoaded">If an image is loaded in the model</param>
+        public void UpdateUIState(bool inDrawingMode, bool canUndo, bool canRedo, bool imageLoaded)
+        {
+            Dictionary<String, MainWindow.ButtonState> dict = new Dictionary<String, MainWindow.ButtonState> {
+                {"canvasButton", MainWindow.ButtonState.Enabled }, {"drawButton", MainWindow.ButtonState.Disabled}, {"deleteButton",MainWindow.ButtonState.Disabled },
+                {"undoButton", MainWindow.ButtonState.Disabled },{"redoButton",  MainWindow.ButtonState.Disabled}};
+
+            if (imageLoaded)
+            {
+                if (inDrawingMode)
+                {
+                    dict["drawButton"] = MainWindow.ButtonState.Active;
+                    dict["deleteButton"] = MainWindow.ButtonState.Enabled;
+                }
+                else
+                {
+                    dict["drawButton"] = MainWindow.ButtonState.Enabled;
+                    dict["deleteButton"] = MainWindow.ButtonState.Active;
+                }
+                if (canUndo) { dict["undoButton"] = MainWindow.ButtonState.Enabled; }
+                if (canRedo) { dict["redoButton"] = MainWindow.ButtonState.Enabled; }
+            }
+            foreach (KeyValuePair<String, MainWindow.ButtonState> entry in dict)
+            {
+                programView.SetToolStripButtonStatus(entry.Key, entry.Value);
+            }
+        }
+
+        /// <summary>
+        /// Is called by the model when the left image is changed.
+        /// </summary>
+        /// <param name="img">The new image.</param>
+        public void UpdateLeftImage(Image img)
+        {
+            programView.DisplayInLeftPictureBox(img);
+        }
+
+        /// <summary>
+        /// Is called by the model when the right image is changed.
+        /// </summary>
+        /// <param name="img">The new image.</param>
+        public void UpdateRightImage(Image img)
+        {
+            programView.DisplayInRightPictureBox(img);
+        }
+
+        /// <summary>
+        /// Pass-trough function to display an info message in the view.
+        /// </summary>
+        /// <param name="msg">The message.</param>
+        public void PassMessageToView(String msg)
+        {
+            programView.ShowInfoMessage(msg);
+        }
+
+        /// <summary>
+        /// Pass-trough function to update the display of the last action taken.
+        /// </summary>
+        /// <param name="msg">The new last action taken.</param>
+        public void PassLastActionTaken(String msg)
+        {
+            programView.SetLastActionTakenText(msg);
+        }
+
+        /*************************/
+        /*** HELPING FUNCTIONS ***/
+        /*************************/
+
+        /// <summary>
+        /// A function that calculates the coordinates of a point on a zoomed in image.
+        /// </summary>
+        /// <param name="">The position of the mouse cursor</param>
+        /// <returns>The real coordinates of the mouse cursor on the image</returns>
+        private Point ConvertCoordinates(Point cursorPosition)
+        {
+            var rightImageDimensions = programModel.GetRightImageDimensions();
+            Point realCoordinates = new Point(0, 0);
+
+            int widthImage = rightImageDimensions.Item1;
+            int heightImage = rightImageDimensions.Item2;
+            int widthBox = programModel.rightImageBoxWidth;
+            int heightBox = programModel.rightImageBoxHeight;
+
+            if (heightImage == 0 && widthImage == 0)
+            {
+                return cursorPosition;
+            }
+
+            float imageRatio = (float)widthImage / (float)heightImage;
+            float containerRatio = (float)widthBox / (float)heightBox;
+
+            if (imageRatio >= containerRatio)
+            {
+                //Image is wider than it is high
+                float zoomFactor = (float)widthImage / (float)widthBox;
+                float scaledHeight = heightImage / zoomFactor;
+                float filler = (heightBox - scaledHeight) / 2;
+                realCoordinates.X = (int)(cursorPosition.X * zoomFactor);
+                realCoordinates.Y = (int)((cursorPosition.Y - filler) * zoomFactor);
+            }
+            else
+            {
+                //Image is higher than it is wide
+                float zoomFactor = (float)heightImage / (float)heightBox;
+                float scaledWidth = widthImage / zoomFactor;
+                float filler = (widthBox - scaledWidth) / 2;
+                realCoordinates.X = (int)((cursorPosition.X - filler) * zoomFactor);
+                realCoordinates.Y = (int)(cursorPosition.Y * zoomFactor);
+            }
+            return realCoordinates;
+        }
+    }
+}

+ 70 - 0
SketchAssistant/SketchAssistantWPF/MVP_View.cs

@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Controls;
+
+namespace SketchAssistantWPF
+{
+    public interface MVP_View
+    {
+
+        /// <summary>
+        /// Enables the timer of the View, which will tick the Presenter.
+        /// </summary>
+        void EnableTimer();
+
+        /// <summary>
+        /// A function that opens a file dialog and returns the filename.
+        /// </summary>
+        /// <param name="Filter">The filter that should be applied to the new Dialog.</param>
+        /// <returns>Returns the FileName and the SafeFileName if the user correctly selects a file, 
+        /// else returns a tuple with empty strigns</returns>
+        Tuple<String, String> openNewDialog(String Filter);
+
+        /// <summary>
+        /// Sets the contents of the load status indicator label.
+        /// </summary>
+        /// <param name="message">The new contents</param>
+        void SetToolStripLoadStatus(String message);
+
+        /// <summary>
+        /// Sets the contents of the last action taken indicator label.
+        /// </summary>
+        /// <param name="message">The new contents</param>
+        void SetLastActionTakenText(String message);
+
+        /// <summary>
+        /// Changes the states of a tool strip button.
+        /// </summary>
+        /// <param name="buttonName">The name of the button.</param>
+        /// <param name="state">The new state of the button.</param>
+        void SetToolStripButtonStatus(String buttonName, MainWindow.ButtonState state);
+
+        /// <summary>
+        /// Displays an image in the left Picture box.
+        /// </summary>
+        /// <param name="img">The new image.</param>
+        void DisplayInLeftPictureBox(Image img);
+
+        /// <summary>
+        /// Displays an image in the right Picture box.
+        /// </summary>
+        /// <param name="img">The new image.</param>
+        void DisplayInRightPictureBox(Image img);
+
+        /// <summary>
+        /// shows the given info message in a popup and asks the user to aknowledge it
+        /// </summary>
+        /// <param name="message">the message to show</param>
+        void ShowInfoMessage(String message);
+
+        /// <summary>
+        /// Shows a warning box with the given message (Yes/No Buttons)and returns true if the user aknowledges it.
+        /// </summary>
+        /// <param name="message">The message of the warning.</param>
+        /// <returns>True if the user confirms (Yes), negative if he doesn't (No)</returns>
+        bool ShowWarning(String message);
+    }
+}

+ 53 - 0
SketchAssistant/SketchAssistantWPF/MainWindow.xaml

@@ -0,0 +1,53 @@
+<Window x:Class="SketchAssistantWPF.MainWindow"
+        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        xmlns:local="clr-namespace:SketchAssistantWPF"
+        mc:Ignorable="d"
+        Title="MainWindow" Height="450" Width="800">
+    <Grid>
+        <Grid.ColumnDefinitions>
+            <ColumnDefinition Width="auto"/>
+            <ColumnDefinition Width="auto"/>
+            <ColumnDefinition Width="auto"/>
+            <ColumnDefinition Width="*"/>
+            <ColumnDefinition Width="auto"/>
+            <ColumnDefinition Width="auto"/>
+            <ColumnDefinition Width="auto"/>
+        </Grid.ColumnDefinitions>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="auto"/>
+            <RowDefinition Height="auto"/>
+            <RowDefinition Height="auto"/>
+            <RowDefinition Height="*"/>
+            <RowDefinition Height="auto"/>
+            <RowDefinition Height="auto"/>
+        </Grid.RowDefinitions>
+        <DockPanel Grid.Column="0" Grid.ColumnSpan="7">
+            <ToolBarTray DockPanel.Dock="Top">
+                <ToolBar x:Name="MenuToolbar" Grid.Column="0" Grid.ColumnSpan="4">
+                    <Menu>
+                        <MenuItem x:Name="LoadMenuButton" Header="Load">
+                            <MenuItem Header="Load SVG File"/>
+                        </MenuItem>
+                        <MenuItem x:Name="EditMenuButton" Header="Edit">
+                            <MenuItem Header="New Canvas"/>
+                            <MenuItem Header="Undo"/>
+                            <MenuItem Header="Redo"/>
+                        </MenuItem>
+                    </Menu>
+                </ToolBar>
+                <ToolBar x:Name="DrawingToolBar" Grid.Column="4" Grid.ColumnSpan="3">
+                    <Button x:Name="CanvasButton" Command="ApplicationCommands.New" ToolTip="Create a new Canvas" Content="New Canvas" Click="CanvasButton_Click" />
+                    <Button x:Name="DrawButton" Command="ApplicationCommands.New" ToolTip="Enter Drawing Mode" Content="Draw" Click="DrawButton_Click"/>
+                    <Button x:Name="DeleteButton" Command="ApplicationCommands.New" ToolTip="Enter Deletion Mode" Content="Delete" Click="DeleteButton_Click" />
+                    <Button x:Name="UndoButton" Command="ApplicationCommands.New" ToolTip="Undo the last action" Content="Undo" Click="UndoButton_Click" />
+                    <Button x:Name="RedoButton" Command="ApplicationCommands.New" ToolTip="Redo the last undone action" Content="Redo" Click="RedoButton_Click" />
+                </ToolBar>
+            </ToolBarTray>
+        </DockPanel>
+        <Image Name="LeftImageBox"/>
+        <Image Name="RightImageBox" />
+    </Grid>
+</Window>

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

@@ -0,0 +1,137 @@
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace SketchAssistantWPF
+{
+    /// <summary>
+    /// Interaction logic for MainWindow.xaml
+    /// </summary>
+    public partial class MainWindow : Window, MVP_View
+    {
+        public MainWindow()
+        {
+            InitializeComponent();
+            ProgramPresenter = new MVP_Presenter(this);
+        }
+
+        public enum ButtonState
+        {
+            Enabled,
+            Disabled,
+            Active
+        }
+
+        /// <summary>
+        /// Dialog to select a file.
+        /// </summary>
+        OpenFileDialog openFileDialog = new OpenFileDialog();
+        /// <summary>
+        /// All Lines in the current session
+        /// </summary>
+        List<Tuple<bool, Line>> rightLineList = new List<Tuple<bool, Line>>();
+        /// <summary>
+        /// Queue for the cursorPositions
+        /// </summary>
+        Queue<Point> cursorPositions = new Queue<Point>();
+        /// <summary>
+        /// The Presenter Component of the MVP-Model
+        /// </summary>
+        MVP_Presenter ProgramPresenter;
+
+        /********************************************/
+        /*** WINDOW SPECIFIC FUNCTIONS START HERE ***/
+        /********************************************/
+
+        private void RedoButton_Click(object sender, RoutedEventArgs e)
+        {
+
+        }
+
+        private void UndoButton_Click(object sender, RoutedEventArgs e)
+        {
+
+        }
+
+        private void DeleteButton_Click(object sender, RoutedEventArgs e)
+        {
+
+        }
+
+        private void DrawButton_Click(object sender, RoutedEventArgs e)
+        {
+
+        }
+
+        /// <summary>
+        /// Button to create a new Canvas. Will create an empty image 
+        /// which is the size of the left image, if there is one.
+        /// If there is no image loaded the canvas will be the size of the right picture box
+        /// </summary>
+        private void CanvasButton_Click(object sender, RoutedEventArgs e)
+        {
+            ProgramPresenter.NewCanvas();
+        }
+
+        /*************************/
+        /*** PRESENTER -> VIEW ***/
+        /*************************/
+
+        public void DisplayInLeftPictureBox(Image img)
+        {
+            throw new NotImplementedException();
+        }
+
+        public void DisplayInRightPictureBox(Image img)
+        {
+            throw new NotImplementedException();
+        }
+
+        public void EnableTimer()
+        {
+            throw new NotImplementedException();
+        }
+
+        public Tuple<string, string> openNewDialog(string Filter)
+        {
+            throw new NotImplementedException();
+        }
+
+        public void SetLastActionTakenText(string message)
+        {
+            throw new NotImplementedException();
+        }
+
+        public void SetToolStripButtonStatus(string buttonName, MainWindow.ButtonState state)
+        {
+            throw new NotImplementedException();
+        }
+
+        public void SetToolStripLoadStatus(string message)
+        {
+            throw new NotImplementedException();
+        }
+
+        public void ShowInfoMessage(string message)
+        {
+            throw new NotImplementedException();
+        }
+
+        public bool ShowWarning(string message)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 55 - 0
SketchAssistant/SketchAssistantWPF/Properties/AssemblyInfo.cs

@@ -0,0 +1,55 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("SketchAssistantWPF")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("SketchAssistantWPF")]
+[assembly: AssemblyCopyright("Copyright ©  2019")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components.  If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+//In order to begin building localizable applications, set
+//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
+//inside a <PropertyGroup>.  For example, if you are using US english
+//in your source files, set the <UICulture> to en-US.  Then uncomment
+//the NeutralResourceLanguage attribute below.  Update the "en-US" in
+//the line below to match the UICulture setting in the project file.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+    ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+                                     //(used if a resource is not found in the page,
+                                     // or application resource dictionaries)
+    ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+                                              //(used if a resource is not found in the page,
+                                              // app, or any theme specific resource dictionaries)
+)]
+
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 71 - 0
SketchAssistant/SketchAssistantWPF/Properties/Resources.Designer.cs

@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace SketchAssistantWPF.Properties
+{
+
+
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources
+    {
+
+        private static global::System.Resources.ResourceManager resourceMan;
+
+        private static global::System.Globalization.CultureInfo resourceCulture;
+
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources()
+        {
+        }
+
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager
+        {
+            get
+            {
+                if ((resourceMan == null))
+                {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SketchAssistantWPF.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture
+        {
+            get
+            {
+                return resourceCulture;
+            }
+            set
+            {
+                resourceCulture = value;
+            }
+        }
+    }
+}

+ 117 - 0
SketchAssistant/SketchAssistantWPF/Properties/Resources.resx

@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 30 - 0
SketchAssistant/SketchAssistantWPF/Properties/Settings.Designer.cs

@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace SketchAssistantWPF.Properties
+{
+
+
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+    {
+
+        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+        public static Settings Default
+        {
+            get
+            {
+                return defaultInstance;
+            }
+        }
+    }
+}

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

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

+ 98 - 0
SketchAssistant/SketchAssistantWPF/SketchAction.cs

@@ -0,0 +1,98 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SketchAssistantWPF
+{
+    public class SketchAction
+    {
+        //Types of possible actions
+        public enum ActionType
+        {
+            Draw,
+            Delete,
+            Start
+        }
+        //Type of this action
+        private ActionType thisAction;
+        //ID of the Line affected
+        private HashSet<int> lineIDs;
+
+        /// <summary>
+        /// Constructor for a new action with multiple lines affected.
+        /// </summary>
+        /// <param name="theAction">The type of action, if it is ActionType.Start the affectedIDs will be ignored.</param>
+        /// <param name="affectedID">The IDs of the lines affected.</param>
+        public SketchAction(ActionType theAction, HashSet<int> affectedIDs)
+        {
+            thisAction = theAction;
+            if (theAction.Equals(ActionType.Start)) { lineIDs = new HashSet<int>(); }
+            else { lineIDs = new HashSet<int>(affectedIDs); }
+        }
+
+        /// <summary>
+        /// Constructor for a new action with one line affected.
+        /// </summary>
+        /// <param name="theAction">The type of action, if it is ActionType.Start the affectedID will be ignored.</param>
+        /// <param name="affectedID">The ID of the affected line.</param>
+        public SketchAction(ActionType theAction, int affectedID)
+        {
+            thisAction = theAction;
+            if (theAction.Equals(ActionType.Start)) { lineIDs = new HashSet<int>(); }
+            else
+            {
+                lineIDs = new HashSet<int>();
+                lineIDs.Add(affectedID);
+            }
+        }
+
+        /// <summary>
+        /// Fetches the type of this action.
+        /// </summary>
+        /// <returns>The type of this action.</returns>
+        public ActionType GetActionType()
+        {
+            return thisAction;
+        }
+
+        /// <summary>
+        /// Fetches the IDs of the lines affected by this action.
+        /// </summary>
+        /// <returns>The IDs of the lines affected by this action. An empty set if there is no line affected.</returns>
+        public HashSet<int> GetLineIDs()
+        {
+            return lineIDs;
+        }
+
+        /// <summary>
+        /// Get the information about this action.
+        /// </summary>
+        /// <returns>A String describing what happend at this action.</returns>
+        public String GetActionInformation()
+        {
+            String returnString;
+            switch (thisAction)
+            {
+                case ActionType.Start:
+                    returnString = "A new canvas was created.";
+                    break;
+                case ActionType.Draw:
+                    returnString = "Line number " + lineIDs.First().ToString() + " was drawn.";
+                    break;
+                case ActionType.Delete:
+                    if (lineIDs.Count == 1) { returnString = "Line number " + lineIDs.First().ToString() + " was deleted."; }
+                    else
+                    {
+                        returnString = "Several Lines were deleted.";
+                    }
+                    break;
+                default:
+                    returnString = "There is no information available for this action.";
+                    break;
+            }
+            return returnString;
+        }
+    }
+}

+ 107 - 0
SketchAssistant/SketchAssistantWPF/SketchAssistantWPF.csproj

@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{EE53AE79-2AA0-4F43-9638-1789B189D5C3}</ProjectGuid>
+    <OutputType>WinExe</OutputType>
+    <RootNamespace>SketchAssistantWPF</RootNamespace>
+    <AssemblyName>SketchAssistantWPF</AssemblyName>
+    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <WarningLevel>4</WarningLevel>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+    <Deterministic>true</Deterministic>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xaml">
+      <RequiredTargetFramework>4.0</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="WindowsBase" />
+    <Reference Include="PresentationCore" />
+    <Reference Include="PresentationFramework" />
+  </ItemGroup>
+  <ItemGroup>
+    <ApplicationDefinition Include="App.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </ApplicationDefinition>
+    <Compile Include="ActionHistory.cs" />
+    <Compile Include="FileImporter.cs" />
+    <Compile Include="FileImporterException.cs" />
+    <Compile Include="GeometryCalculator.cs" />
+    <Compile Include="SketchAction.cs" />
+    <Page Include="MainWindow.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Compile Include="App.xaml.cs">
+      <DependentUpon>App.xaml</DependentUpon>
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="Line.cs" />
+    <Compile Include="MainWindow.xaml.cs">
+      <DependentUpon>MainWindow.xaml</DependentUpon>
+      <SubType>Code</SubType>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="MVP_Model.cs" />
+    <Compile Include="MVP_Presenter.cs" />
+    <Compile Include="MVP_View.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTime>True</DesignTime>
+      <DependentUpon>Resources.resx</DependentUpon>
+    </Compile>
+    <Compile Include="Properties\Settings.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Settings.settings</DependentUpon>
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+    </Compile>
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+    </EmbeddedResource>
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project>

+ 13 - 0
userstory16.md

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