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.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
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;
ImageDimension CanvasSizeLeft = new ImageDimension(0,0);
ImageDimension CanvasSizeRight = new ImageDimension(0, 0);
ImageDimension ImageSizeLeft = new ImageDimension(0, 0);
ImageDimension ImageSizeRight = new ImageDimension(0, 0);
List ImageSimilarity = new List();
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);
//Initialize Class Variables
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.UpdateSizes(CanvasSizeRight);
programModel.ResizeEvent(CanvasSizeLeft, 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.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);
}
///
/// 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(CanvasSizeLeft, 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 Mouse event arguments.
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)
{
switch (mouseAction)
{
case MouseAction.Down:
programModel.MouseDown();
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.MouseUp(points);
}
else
{
programModel.MouseUp(true);
}
break;
case MouseAction.Up_Invalid:
programModel.MouseUp(false);
break;
default:
break;
}
}
/************************************/
/*** FUNCTIONS MODEL -> PRESENTER ***/
/************************************/
///
/// Return the position of the cursor
///
/// The position of the cursor
public Point GetCursorPosition()
{
return programView.GetCursorPosition();
}
///
/// 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));
}
}
///
/// 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);
}
///
/// 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
public void UpdateUIState(bool inDrawingMode, bool canUndo, bool canRedo, bool canvasActive, bool graphicLoaded)
{
Dictionary dict = new Dictionary {
{"canvasButton", MainWindow.ButtonState.Enabled }, {"drawButton", MainWindow.ButtonState.Disabled}, {"deleteButton",MainWindow.ButtonState.Disabled },
{"undoButton", MainWindow.ButtonState.Disabled },{"redoButton", MainWindow.ButtonState.Disabled}};
if (canvasActive)
{
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 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-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();
}
///
/// 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 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;
switch (name)
{
case "optipoint":
shape = ((MainWindow)programView).OverlayDictionary["optipoint"];
visibility = 0.5; xDif = 2.5; yDif = 2.5;
break;
case "startpoint":
shape = ((MainWindow)programView).OverlayDictionary["startpoint"];
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;
Point point = ConvertRightCanvasCoordinateToOverlay(position);
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;
}
}
}
}