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