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; using System.Windows.Media; using System.Windows.Shapes; using OptiTrack; using System.Windows.Ink; namespace SketchAssistantWPF { public class MVP_Presenter { /// /// The View of the MVP-Model, in this case Form1. /// MVP_View programView; /// /// The Model of the MVP-Model. /// MVP_Model programModel; /// /// A dictionary connecting the id of an InternalLine with the respective Polyline in the right canvas. /// Dictionary rightPolyLines; /// /// The actual size of the left canvas. /// ImageDimension canvasSizeLeft = new ImageDimension(0, 0); /// /// The actual size of the right canvas. /// ImageDimension canvasSizeRight = new ImageDimension(0, 0); /// /// A list of line similarities, resulting in the similarity of the whole image. /// List imageSimilarity = new List(); /// /// The lines in the left canvas. /// List leftLines = new List(); /*******************/ /*** ENUMERATORS ***/ /*******************/ public enum MouseAction { Down, Up, Up_Invalid, Move } /***********************/ /*** CLASS VARIABLES ***/ /***********************/ /// /// Instance of FileImporter to handle drawing imports. /// private FileImporter fileImporter; public MVP_Presenter(MVP_View form) { programView = form; programModel = new MVP_Model(this); fileImporter = new FileImporter(); } /***********************************/ /*** FUNCTIONS VIEW -> PRESENTER ***/ /***********************************/ /// /// Pass-trough function to update the appropriate information of the model, when the window is resized. /// /// The new size of the left picture box. /// The new size of the left picture box. public void Resize(Tuple leftPBS, Tuple rightPBS) { canvasSizeLeft.ChangeDimension(leftPBS.Item1, leftPBS.Item2); canvasSizeRight.ChangeDimension(rightPBS.Item1, rightPBS.Item2); programModel.ResizeEvent(canvasSizeRight); } /// /// Display a new FileDialog to a svg drawing. /// /// True if loading was a success public bool SVGToolStripMenuItemClick() { var okToContinue = true; bool returnval = false; if (programModel.HasUnsavedProgress()) { okToContinue = programView.ShowWarning("You have unsaved progress. Continue?"); } if (okToContinue) { var fileNameTup = programView.openNewDialog("Scalable Vector Graphics|*.svg"); if (!fileNameTup.Item1.Equals("") && !fileNameTup.Item2.Equals("")) { programView.SetToolStripLoadStatus(fileNameTup.Item2); try { Tuple> values = fileImporter.ParseSVGInputFile(fileNameTup.Item1, programModel.leftImageBoxWidth, programModel.leftImageBoxHeight); values.Item3.ForEach(line => line.MakePermanent(0)); //Make all lines permanent programModel.SetLeftLineList(values.Item1, values.Item2, values.Item3); programModel.ResizeEvent(canvasSizeRight); programModel.ResetRightImage(); programModel.CanvasActivated(); programModel.ChangeState(true); programView.EnableTimer(); ClearRightLines(); returnval = true; } catch (FileImporterException ex) { programView.ShowInfoMessage(ex.ToString()); } catch (Exception ex) { programView.ShowInfoMessage("exception occured while trying to parse svg file:\n\n" + ex.ToString() + "\n\n" + ex.StackTrace); } } } return returnval; } /// /// Pass-trough function to change the drawing state of the model. /// /// Indicates if the program is in drawing (true) or deletion (false) mode. public void ChangeState(bool NowDrawing) { programModel.ChangeState(NowDrawing); } /// /// Gets whether the program is in drawing state or not. /// /// public bool GetDrawingState() { return programModel.inDrawingMode; } /// /// Gets whether Optitrack is in use or not. /// /// Return optiTrackInUse public bool GetOptitrackActive() { return programModel.optiTrackInUse; } /// /// Pass-through function to change the OptiTrack-in-use state of the model /// public void ChangeOptiTrack(bool usingOptiTrack) { if (programModel.optitrackAvailable) { programModel.SetOptiTrack(usingOptiTrack); } } /// /// Pass-trough function to undo an action. /// public void Undo() { programModel.Undo(); } /// /// Pass-trough function to redo an action. /// public void Redo() { programModel.Redo(); } /// /// Pass-trough function for ticking the model. /// public void Tick() { programModel.Tick(); } /// /// Checks if there is unsaved progress, and promts the model to generate a new canvas if not. /// public void NewCanvas() { var okToContinue = true; if (programModel.HasUnsavedProgress()) { okToContinue = programView.ShowWarning("You have unsaved progress. Continue?"); } if (okToContinue) { programModel.ResizeEvent(canvasSizeRight); programModel.ResetRightImage(); programModel.CanvasActivated(); programModel.ChangeState(true); programView.EnableTimer(); ClearRightLines(); } } /// /// Pass-trough when the mouse is moved. /// /// The action which is sent by the View. /// The position of the mouse. public void MouseEvent(MouseAction mouseAction, Point position) { switch (mouseAction) { case MouseAction.Move: programModel.SetCurrentCursorPosition(position); break; default: break; } } /// /// Pass-trough function that calls the correct Mouse event of the model, when the mouse is clicked. /// /// The action which is sent by the View. /// The Strokes. public void MouseEvent(MouseAction mouseAction, StrokeCollection strokes) { if (!programModel.optiTrackInUse) { switch (mouseAction) { case MouseAction.Down: programModel.StartNewLine(); break; case MouseAction.Up: if (strokes.Count > 0) { StylusPointCollection sPoints = strokes.First().StylusPoints; List points = new List(); foreach (StylusPoint p in sPoints) points.Add(new Point(p.X, p.Y)); programModel.FinishCurrentLine(points); } else { programModel.FinishCurrentLine(true); } break; case MouseAction.Up_Invalid: programModel.FinishCurrentLine(false); break; default: break; } } } /************************************/ /*** FUNCTIONS MODEL -> PRESENTER ***/ /************************************/ /// /// Return the position of the cursor /// /// The position of the cursor public Point GetCursorPosition() { return programView.GetCursorPosition(); } /// /// Updates the currentline /// /// The points of the current line. public void UpdateCurrentLine(List linepoints) { Polyline currentLine = new Polyline(); currentLine.Stroke = Brushes.Black; currentLine.Points = new PointCollection(linepoints); programView.DisplayCurrLine(currentLine); } /// /// Clears all Lines in the right canvas. /// public void ClearRightLines() { programView.RemoveAllRightLines(); rightPolyLines = new Dictionary(); //Reset the similarity display UpdateSimilarityScore(Double.NaN); } /// /// A function to update the displayed lines in the right canvas. /// public void UpdateRightLines(List> lines) { foreach (Tuple tup in lines) { var status = tup.Item1; var line = tup.Item2; if (!rightPolyLines.ContainsKey(line.GetID())) { if (!line.isPoint) { Polyline newLine = new Polyline(); newLine.Points = line.GetPointCollection(); rightPolyLines.Add(line.GetID(), newLine); programView.AddNewLineRight(newLine); } else { Ellipse newPoint = new Ellipse(); rightPolyLines.Add(line.GetID(), newPoint); programView.AddNewPointRight(newPoint, line); } } SetVisibility(rightPolyLines[line.GetID()], status); } //Calculate similarity scores UpdateSimilarityScore(Double.NaN); var templist = lines.Where(tup => tup.Item1).ToList(); if (leftLines.Count > 0) { for (int i = 0; i < leftLines.Count; i++) { if (templist.Count == i) break; UpdateSimilarityScore(GeometryCalculator.CalculateSimilarity(templist[i].Item2, leftLines[i])); } } else if (templist.Count > 1) { UpdateSimilarityScore(GeometryCalculator.CalculateSimilarity(templist[templist.Count - 2].Item2, templist[templist.Count - 1].Item2)); } } /// /// A function to update the displayed lines in the left canvas. /// public void UpdateLeftLines(List lines) { programView.RemoveAllLeftLines(); foreach (InternalLine line in lines) { Polyline newLine = new Polyline(); newLine.Stroke = Brushes.Black; newLine.Points = line.GetPointCollection(); programView.AddNewLineLeft(newLine); } programView.SetCanvasState("LeftCanvas", true); programView.SetCanvasState("RightCanvas", true); leftLines = lines; } /// /// 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. /// /// If the model is in Drawing Mode /// If actions in the model can be undone /// If actions in the model can be redone /// If the right canvas is active /// If an image is loaded in the model /// If there is an optitrack system available /// If the optitrack system is active public void UpdateUIState(bool inDrawingMode, bool canUndo, bool canRedo, bool canvasActive, bool graphicLoaded, bool optiTrackAvailable, bool optiTrackInUse) { Dictionary dict = new Dictionary { {"canvasButton", MainWindow.ButtonState.Enabled }, {"drawButton", MainWindow.ButtonState.Disabled}, {"deleteButton",MainWindow.ButtonState.Disabled }, {"undoButton", MainWindow.ButtonState.Disabled },{"redoButton", MainWindow.ButtonState.Disabled}, {"drawWithOptiButton", MainWindow.ButtonState.Disabled}}; if (canvasActive) { if (inDrawingMode) { if (optiTrackAvailable) { if (optiTrackInUse) { dict["drawButton"] = MainWindow.ButtonState.Enabled; dict["drawWithOptiButton"] = MainWindow.ButtonState.Active; dict["deleteButton"] = MainWindow.ButtonState.Enabled; } else { dict["drawButton"] = MainWindow.ButtonState.Active; dict["drawWithOptiButton"] = MainWindow.ButtonState.Enabled; dict["deleteButton"] = MainWindow.ButtonState.Enabled; } } else { dict["drawButton"] = MainWindow.ButtonState.Active; dict["deleteButton"] = MainWindow.ButtonState.Enabled; } } else { dict["drawButton"] = MainWindow.ButtonState.Enabled; dict["deleteButton"] = MainWindow.ButtonState.Active; if (optiTrackAvailable) { dict["drawWithOptiButton"] = MainWindow.ButtonState.Enabled; } } if (canUndo) { dict["undoButton"] = MainWindow.ButtonState.Enabled; } if (canRedo) { dict["redoButton"] = MainWindow.ButtonState.Enabled; } } foreach (KeyValuePair entry in dict) { programView.SetToolStripButtonStatus(entry.Key, entry.Value); } programView.SetCanvasState("RightCanvas", canvasActive); programView.SetCanvasState("LeftCanvas", graphicLoaded); } /// /// Pass-trough function to display an info message in the view. /// /// The message. public void PassMessageToView(String msg) { programView.ShowInfoMessage(msg); } /// /// Pass-through function to desplay an Warning message in the view /// /// The message. /// True if the user confirms (Yes), negative if he doesn't (No) public bool PassWarning(String msg) { return programView.ShowWarning(msg); } /// /// Pass-trough function to update the display of the last action taken. /// /// The new last action taken. public void PassLastActionTaken(String msg) { programView.SetLastActionTakenText(msg); } /// /// Passes whether or not the mouse is pressed. /// /// Whether or not the mouse is pressed public bool IsMousePressed() { return programView.IsMousePressed(); } public void PassOptiTrackMessage(String stringToPass) { programView.SetOptiTrackText(stringToPass); } /// /// Update the similarity score displayed in the UI. /// /// Score will be reset if NaN is passed, /// will be ignored if the score is not between 0 and 1 public void UpdateSimilarityScore(double score) { if (Double.IsNaN(score)) { imageSimilarity.Clear(); programView.SetImageSimilarityText(""); } else { if (score >= 0 && score <= 1) imageSimilarity.Add(score); programView.SetImageSimilarityText((imageSimilarity.Sum() / imageSimilarity.Count).ToString()); } } /// /// Change the color of an overlay item. /// /// The name of the item in the overlay dictionary /// The color of the item public void SetOverlayColor(String name, Brush color) { Shape shape = new Ellipse(); if(((MainWindow)programView).overlayDictionary.ContainsKey(name)) { shape = ((MainWindow)programView).overlayDictionary[name]; if(name.Substring(0, 7).Equals("dotLine")) shape.Stroke = color; else shape.Fill = color; } } /// /// Change the properties of an overlay item. /// /// The name of the item in the overlay dictionary /// If the element should be visible /// The new position of the element public void SetOverlayStatus(String name, bool visible, Point position) { Shape shape = new Ellipse(); double visibility = 1; double xDif = 0; double yDif = 0; Point point = ConvertRightCanvasCoordinateToOverlay(position); switch (name) { case "optipoint": shape = ((MainWindow)programView).overlayDictionary["optipoint"]; visibility = 0.75; xDif = 2.5; yDif = 2.5; break; case "startpoint": shape = ((MainWindow)programView).overlayDictionary["startpoint"]; xDif = ((MainWindow)programView).markerRadius; yDif = xDif; break; case "endpoint": shape = ((MainWindow)programView).overlayDictionary["endpoint"]; xDif = ((MainWindow)programView).markerRadius; yDif = xDif; break; default: Console.WriteLine("Unknown Overlay Item. Please check in MVP_Presenter if this item exists."); return; } if (visible) shape.Opacity = visibility; else shape.Opacity = 0.00001; shape.Margin = new Thickness(point.X - xDif, point.Y - yDif, 0, 0); } /// /// Change the properties of an overlay item. /// /// The name of the item in the overlay dictionary /// If the element should be visible /// The new position of the element /// The new position of the element public void SetOverlayStatus(String name, bool visible, Point pos1, Point pos2) { Shape shape = new Ellipse(); switch (name.Substring(0, 7)) { case "dotLine": if (((MainWindow)programView).overlayDictionary.ContainsKey(name)) { shape = ((MainWindow)programView).overlayDictionary[name]; break; } goto default; default: SetOverlayStatus(name, visible, pos1); return; } if (visible) shape.Opacity = 1; else shape.Opacity = 0.00001; Point p1 = ConvertRightCanvasCoordinateToOverlay(pos1); Point p2 = ConvertRightCanvasCoordinateToOverlay(pos2); ((Line)shape).X1 = p1.X; ((Line)shape).X2 = p2.X; ((Line)shape).Y1 = p1.Y; ((Line)shape).Y2 = p2.Y; } /// /// Move the optitrack pointer to a new position. /// /// Position relative to the Rightcanvas public void MoveOptiPoint(Point position) { Point point = ConvertRightCanvasCoordinateToOverlay(position); Shape shape = ((MainWindow)programView).overlayDictionary["optipoint"]; shape.Margin = new Thickness(point.X - 2.5, point.Y - 2.5, 0, 0); } /*************************/ /*** HELPING FUNCTIONS ***/ /*************************/ /// /// Convert point relative to the right canvas to a point relative to the overlay canvas. /// /// The point to convert. /// The respective overlay canvas. private Point ConvertRightCanvasCoordinateToOverlay(Point p) { MainWindow main = (MainWindow)programView; double xDif = (main.CanvasLeftEdge.ActualWidth + main.LeftCanvas.ActualWidth + main.CanvasSeperator.ActualWidth); double yDif = (main.ButtonToolbar.ActualHeight); return new Point(p.X + xDif, p.Y + yDif); } /// /// Sets the visibility of a polyline. /// /// The polyline /// Whether or not it should be visible. private void SetVisibility(Shape line, bool visible) { if (!visible) { line.Opacity = 0.00001; } else { line.Opacity = 1; } } } }