using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
namespace SketchAssistant
{
public class MVP_Model
{
///
/// The Presenter of the MVP-Model.
///
MVP_Presenter programPresenter;
///
/// History of Actions
///
ActionHistory historyOfActions;
///
/// The assistant responsible for the redraw mode
///
RedrawAssistant redrawAss;
/*******************/
/*** ENUMERATORS ***/
/*******************/
/***********************/
/*** CLASS VARIABLES ***/
/***********************/
bool inDrawingMode;
bool mousePressed;
///
/// Size of deletion area
///
int deletionRadius = 5;
///
/// Size of areas marking endpoints of lines in the redraw mode.
///
int markerRadius = 10;
///
/// The Position of the Cursor in the right picture box
///
Point currentCursorPosition;
///
/// The Previous Cursor Position in the right picture box
///
Point previousCursorPosition;
///
/// Queue for the cursorPositions
///
Queue cursorPositions = new Queue();
///
/// Lookup Matrix for checking postions of lines in the image
///
bool[,] isFilledMatrix;
///
/// Lookup Matrix for getting line ids at a certain postions of the image
///
HashSet[,] linesMatrix;
///
/// List of items which will be overlayed over the right canvas.
///
List>> overlayItems;
///
/// Width of the LeftImageBox.
///
public int leftImageBoxWidth;
///
/// Height of the LeftImageBox.
///
public int leftImageBoxHeight;
///
/// Width of the RightImageBox.
///
public int rightImageBoxWidth;
///
/// Height of the RightImageBox.
///
public int rightImageBoxHeight;
//Images
Image leftImage;
List leftLineList;
Image rightImageWithoutOverlay;
Image rightImageWithOverlay;
List> rightLineList;
List currentLine;
public MVP_Model(MVP_Presenter presenter)
{
programPresenter = presenter;
historyOfActions = new ActionHistory(null);
redrawAss = new RedrawAssistant();
}
public void Undo()
{
}
///
/// A function that returns a white canvas for a given width and height.
///
/// The width of the canvas in pixels
/// The height of the canvas in pixels
/// The new canvas
private Image GetEmptyCanvas(int width, int height)
{
Image image;
try
{
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.");
image = new Bitmap(leftImageBoxWidth, leftImageBoxHeight);
}
Graphics graph = Graphics.FromImage(image);
graph.FillRectangle(Brushes.White, 0, 0, width + 10, height + 10);
return image;
}
///
/// Creates an empty Canvas
///
private void DrawEmptyCanvasRight()
{
if (leftImage == null)
{
rightImageWithoutOverlay = GetEmptyCanvas(leftImageBoxWidth, leftImageBoxHeight);
}
else
{
rightImageWithoutOverlay = GetEmptyCanvas(leftImage.Width, leftImage.Height);
}
RefreshRightImage();
}
///
/// Creates an empty Canvas on the left
///
/// width of the new canvas in pixels
/// height of the new canvas in pixels
private void DrawEmptyCanvasLeft(int width, int height)
{
if (width == 0)
{
leftImage = GetEmptyCanvas(leftImageBoxWidth, leftImageBoxHeight);
}
else
{
leftImage = GetEmptyCanvas(width, height);
}
RefreshLeftImage();
}
///
/// A function to refresh the image being displayed in the right picture box, with the current rightImageWithOverlay.
///
/// The new Image
private void RefreshRightImage()
{
programPresenter.UpdateRightImage(rightImageWithOverlay);
}
///
/// A function to refresh the image being displayed in the left picture box, with the current leftImage.
///
/// The new Image
private void RefreshLeftImage()
{
programPresenter.UpdateLeftImage(leftImage);
}
///
/// Redraws all lines in lineList, for which their associated boolean value equals true and calls RedrawRightOverlay.
///
private void RedrawRightImage()
{
var workingCanvas = GetEmptyCanvas(rightImageWithoutOverlay.Width, rightImageWithoutOverlay.Height);
var workingGraph = Graphics.FromImage(workingCanvas);
//Lines
foreach (Tuple 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
RedrawRightOverlay();
RefreshRightImage();
}
///
/// Redraws all elements in the overlay items for which the respective boolean value is true.
///
private void RedrawRightOverlay()
{
var workingCanvas = rightImageWithoutOverlay;
var workingGraph = Graphics.FromImage(workingCanvas);
foreach (Tuple> tup in overlayItems)
{
if (tup.Item1)
{
foreach (Point p in tup.Item2)
{
workingGraph.FillRectangle(Brushes.Green, p.X, p.Y, 1, 1);
}
}
}
rightImageWithOverlay = workingCanvas;
RefreshRightImage();
}
///
/// The function called by the Presenter to change the drawing state of the program.
///
/// The new drawingstate of the program
public void ChangeState(bool nowDrawing)
{
inDrawingMode = nowDrawing;
UpdateUI();
}
///
/// Change the status of whether or not the lines are shown.
///
/// The HashSet containing the affected Line IDs.
/// True if the lines should be shown, false if they should be hidden.
private void ChangeLines(HashSet lines, bool shown)
{
var changed = false;
foreach (int lineId in lines)
{
if (lineId <= rightLineList.Count - 1 && lineId >= 0)
{
rightLineList[lineId] = new Tuple(shown, rightLineList[lineId].Item2);
changed = true;
}
}
if (changed) { RedrawRightImage(); }
}
///
/// A function that populates the matrixes needed for deletion detection with line data.
///
private void RepopulateDeletionMatrixes()
{
if (rightImageWithoutOverlay != null)
{
isFilledMatrix = new bool[rightImageWithoutOverlay.Width, rightImageWithoutOverlay.Height];
linesMatrix = new HashSet[rightImageWithoutOverlay.Width, rightImageWithoutOverlay.Height];
foreach (Tuple lineTuple in rightLineList)
{
if (lineTuple.Item1)
{
lineTuple.Item2.PopulateMatrixes(isFilledMatrix, linesMatrix);
}
}
}
}
/*
///
/// 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.
///
/// The point around which to check.
/// The range around the point. If range is 0, only the point is checked.
/// A List of all lines.
private HashSet CheckDeletionMatrixesAroundPoint(Point p, int range)
{
HashSet returnSet = new HashSet();
foreach (Point pnt in GeometryCalculator.FilledCircleAlgorithm(p, (int)range))
{
if (pnt.X >= 0 && pnt.Y >= 0 && pnt.X < rightImage.Width && pnt.Y < rightImage.Height)
{
if (isFilledMatrix[pnt.X, pnt.Y])
{
returnSet.UnionWith(linesMatrix[pnt.X, pnt.Y]);
}
}
}
return returnSet;
}
///
/// binds the given picture to templatePicture and draws it
///
/// the new template picture, represented as a list of polylines
///
private void BindAndDrawLeftImage(List newTemplatePicture)
{
leftLineList = newTemplatePicture;
foreach (Line l in leftLineList)
{
l.DrawLine(Graphics.FromImage(leftImage));
}
}
///
/// Will calculate the start and endpoints of the given line on the right canvas.
///
/// The line.
/// The size of the circle with which the endpoints of the line are marked.
private Tuple, HashSet> CalculateStartAndEnd(Line line, int size)
{
var circle0 = GeometryCalculator.FilledCircleAlgorithm(line.GetStartPoint(), size);
var circle1 = GeometryCalculator.FilledCircleAlgorithm(line.GetEndPoint(), size);
var currentLineEndings = new Tuple, HashSet>(circle0, circle1);
return currentLineEndings;
}
*/
///
/// A helper Function that updates the markerRadius & deletionRadius, considering the size of the canvas.
///
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);
}
}
///
/// Tells the Presenter to Update the UI
///
private void UpdateUI()
{
programPresenter.UpdateUIState(inDrawingMode, historyOfActions.CanUndo(), historyOfActions.CanRedo(), (rightImageWithoutOverlay != null));
}
///
/// A method to get the dimensions of the right image.
///
/// A tuple containing the width and height of the right image.
public Tuple GetRightImageDimensions()
{
if(rightImageWithoutOverlay != null)
{
return new Tuple(rightImageWithoutOverlay.Width, rightImageWithoutOverlay.Height);
}
else
{
return new Tuple(0, 0);
}
}
public void SetCurrentCursorPosition(Point p)
{
}
/*
///
/// Checks if there is unsaved progess, and warns the user. Returns True if it safe to continue.
///
/// true if there is none, or the user wishes to continue without saving.
/// false if there is progress, and the user doesn't wish to continue.
private bool CheckSavedStatus()
{
if (!historyOfActions.IsEmpty())
{
return (MessageBox.Show("You have unsaved changes, do you wish to continue?",
"Attention", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes);
}
return true;
}
*/
}
}