Form1.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. using System.Windows.Forms;
  10. using System.Text.RegularExpressions;
  11. // This is the code for your desktop app.
  12. // Press Ctrl+F5 (or go to Debug > Start Without Debugging) to run your app.
  13. namespace SketchAssistant
  14. {
  15. public partial class Form1 : Form
  16. {
  17. public Form1()
  18. {
  19. InitializeComponent();
  20. ProgramPresenter = new MVP_Presenter(this);
  21. fileImporter = new FileImporter();
  22. }
  23. /**********************************/
  24. /*** CLASS VARIABLES START HERE ***/
  25. /**********************************/
  26. //important: add new variables only at the end of the list to keep the order of definition consistent with the order in which they are returned by GetAllVariables()
  27. public enum ButtonState
  28. {
  29. Enabled,
  30. Disabled,
  31. Active
  32. }
  33. /// <summary>
  34. /// Different Program States
  35. /// </summary>
  36. public enum ProgramState
  37. {
  38. Idle,
  39. Draw,
  40. Delete
  41. }
  42. /// <summary>
  43. /// Current Program State
  44. /// </summary>
  45. private ProgramState currentState;
  46. /// <summary>
  47. /// instance of FileImporter to handle drawing imports
  48. /// </summary>
  49. private FileImporter fileImporter;
  50. /// <summary>
  51. /// Dialog to select a file.
  52. /// </summary>
  53. OpenFileDialog openFileDialog = new OpenFileDialog();
  54. /// <summary>
  55. /// Image loaded on the left
  56. /// </summary>
  57. private Image leftImage = null;
  58. /// <summary>
  59. /// the graphic shown in the left window, represented as a list of polylines
  60. /// </summary>
  61. private List<Line> leftLineList;
  62. /// <summary>
  63. /// Image on the right
  64. /// </summary>
  65. Image rightImage = null;
  66. /// <summary>
  67. /// Current Line being Drawn
  68. /// </summary>
  69. List<Point> currentLine;
  70. /// <summary>
  71. /// All Lines in the current session
  72. /// </summary>
  73. List<Tuple<bool,Line>> rightLineList = new List<Tuple<bool, Line>>();
  74. /// <summary>
  75. /// Whether the Mouse is currently pressed in the rightPictureBox
  76. /// </summary>
  77. bool mousePressed = false;
  78. /// <summary>
  79. /// The Position of the Cursor in the right picture box
  80. /// </summary>
  81. Point currentCursorPosition;
  82. /// <summary>
  83. /// The Previous Cursor Position in the right picture box
  84. /// </summary>
  85. Point previousCursorPosition;
  86. /// <summary>
  87. /// Queue for the cursorPositions
  88. /// </summary>
  89. Queue<Point> cursorPositions = new Queue<Point>();
  90. /// <summary>
  91. /// The graphic representation of the right image
  92. /// </summary>
  93. Graphics rightGraph = null;
  94. /// <summary>
  95. /// Lookup Matrix for checking postions of lines in the image
  96. /// </summary>
  97. bool[,] isFilledMatrix;
  98. /// <summary>
  99. /// Lookup Matrix for getting line ids at a certain postions of the image
  100. /// </summary>
  101. HashSet<int>[,] linesMatrix;
  102. /// <summary>
  103. /// Size of deletion area
  104. /// </summary>
  105. int deletionRadius = 2;
  106. /// <summary>
  107. /// History of Actions
  108. /// </summary>
  109. ActionHistory historyOfActions;
  110. /// <summary>
  111. /// List of items which will be overlayed over the right canvas.
  112. /// </summary>
  113. List<HashSet<Point>> overlayItems;
  114. /// <summary>
  115. /// The assistant responsible for the redraw mode
  116. /// </summary>
  117. RedrawAssistant redrawAss;
  118. /// <summary>
  119. /// Size of areas marking endpoints of lines in the redraw mode.
  120. /// </summary>
  121. int markerRadius = 10;
  122. /// <summary>
  123. /// The Presenter Component of the MVP-Model
  124. /// </summary>
  125. MVP_Presenter ProgramPresenter;
  126. /******************************************/
  127. /*** FORM SPECIFIC FUNCTIONS START HERE ***/
  128. /******************************************/
  129. private void Form1_Load(object sender, EventArgs e)
  130. {
  131. this.DoubleBuffered = true;
  132. /*
  133. currentState = ProgramState.Idle;
  134. historyOfActions = new ActionHistory(null);
  135. redrawAss = new RedrawAssistant();
  136. UpdateButtonStatus();*/
  137. }
  138. /// <summary>
  139. /// Resize Function connected to the form resize event, will refresh the form when it is resized
  140. /// </summary>
  141. private void Form1_Resize(object sender, System.EventArgs e)
  142. {
  143. ProgramPresenter.Resize(new Tuple<int, int>(pictureBoxLeft.Width, pictureBoxLeft.Height),
  144. new Tuple<int, int>(pictureBoxRight.Width, pictureBoxRight.Height));
  145. /*
  146. this.Refresh();
  147. UpdateSizes();*/
  148. }
  149. //Load button, will open an OpenFileDialog
  150. private void loadToolStripMenuItem_Click(object sender, EventArgs e)
  151. {
  152. ProgramPresenter.LoadToolStripMenuItemClick();
  153. /*
  154. openFileDialog.Filter = "Image|*.jpg;*.png;*.jpeg";
  155. if(openFileDialog.ShowDialog() == DialogResult.OK)
  156. {
  157. toolStripLoadStatus.Text = openFileDialog.SafeFileName;
  158. leftImage = Image.FromFile(openFileDialog.FileName);
  159. pictureBoxLeft.Image = leftImage;
  160. //Refresh the left image box when the content is changed
  161. this.Refresh();
  162. }
  163. UpdateButtonStatus();
  164. */
  165. }
  166. /// <summary>
  167. /// Import button, will open an OpenFileDialog
  168. /// </summary>
  169. private void examplePictureToolStripMenuItem_Click(object sender, EventArgs e)
  170. {
  171. ProgramPresenter.ExamplePictureToolStripMenuItemClick();
  172. /*
  173. if (CheckSavedStatus())
  174. {
  175. openFileDialog.Filter = "Interactive Sketch-Assistant Drawing|*.isad";
  176. if (openFileDialog.ShowDialog() == DialogResult.OK)
  177. {
  178. toolStripLoadStatus.Text = openFileDialog.SafeFileName;
  179. try
  180. {
  181. (int, int, List<Line>) values = fileImporter.ParseISADInputFile(openFileDialog.FileName);
  182. DrawEmptyCanvasLeft(values.Item1, values.Item2);
  183. BindAndDrawLeftImage(values.Item3);
  184. //Match The right canvas to the left
  185. historyOfActions = new ActionHistory(lastActionTakenLabel);
  186. DrawEmptyCanvasRight();
  187. isFilledMatrix = new bool[rightImage.Width, rightImage.Height];
  188. linesMatrix = new HashSet<int>[rightImage.Width, rightImage.Height];
  189. rightLineList = new List<Tuple<bool, Line>>();
  190. //Start the redraw mode
  191. redrawAss = new RedrawAssistant(leftLineList);
  192. UpdateSizes();
  193. overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, -1, false);
  194. RedrawRightImage();
  195. this.Refresh();
  196. }
  197. catch (FileImporterException ex)
  198. {
  199. ShowInfoMessage(ex.ToString());
  200. }
  201. }
  202. }
  203. UpdateButtonStatus();
  204. */
  205. }
  206. /// <summary>
  207. /// Changes the state of the program to drawing
  208. /// </summary>
  209. private void drawButton_Click(object sender, EventArgs e)
  210. {
  211. ProgramPresenter.ChangeState(true);
  212. /*
  213. if(rightImage != null)
  214. {
  215. if (currentState.Equals(ProgramState.Draw))
  216. {
  217. ChangeState(ProgramState.Idle);
  218. }
  219. else
  220. {
  221. ChangeState(ProgramState.Draw);
  222. }
  223. }
  224. UpdateButtonStatus();*/
  225. }
  226. /// <summary>
  227. /// Changes the state of the program to deletion
  228. /// </summary>
  229. private void deleteButton_Click(object sender, EventArgs e)
  230. {
  231. ProgramPresenter.ChangeState(false);
  232. /*
  233. if (rightImage != null)
  234. {
  235. if (currentState.Equals(ProgramState.Delete))
  236. {
  237. ChangeState(ProgramState.Idle);
  238. }
  239. else
  240. {
  241. ChangeState(ProgramState.Delete);
  242. }
  243. }
  244. UpdateButtonStatus();*/
  245. }
  246. /// <summary>
  247. /// Undo an Action.
  248. /// </summary>
  249. private void undoButton_Click(object sender, EventArgs e)
  250. {
  251. ProgramPresenter.Undo();
  252. /*
  253. if (historyOfActions.CanUndo())
  254. {
  255. HashSet<int> affectedLines = historyOfActions.GetCurrentAction().GetLineIDs();
  256. SketchAction.ActionType undoAction = historyOfActions.GetCurrentAction().GetActionType();
  257. switch (undoAction)
  258. {
  259. case SketchAction.ActionType.Delete:
  260. //Deleted Lines need to be shown
  261. ChangeLines(affectedLines, true);
  262. break;
  263. case SketchAction.ActionType.Draw:
  264. //Drawn lines need to be hidden
  265. ChangeLines(affectedLines, false);
  266. break;
  267. default:
  268. break;
  269. }
  270. overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, -1, false);
  271. RedrawRightImage();
  272. }
  273. historyOfActions.MoveAction(true);
  274. UpdateButtonStatus();*/
  275. }
  276. /// <summary>
  277. /// Redo an Action.
  278. /// </summary>
  279. private void redoButton_Click(object sender, EventArgs e)
  280. {
  281. ProgramPresenter.Redo();
  282. /*
  283. if (historyOfActions.CanRedo())
  284. {
  285. historyOfActions.MoveAction(false);
  286. HashSet<int> affectedLines = historyOfActions.GetCurrentAction().GetLineIDs();
  287. SketchAction.ActionType redoAction = historyOfActions.GetCurrentAction().GetActionType();
  288. switch (redoAction)
  289. {
  290. case SketchAction.ActionType.Delete:
  291. //Deleted Lines need to be redeleted
  292. ChangeLines(affectedLines, false);
  293. break;
  294. case SketchAction.ActionType.Draw:
  295. //Drawn lines need to be redrawn
  296. ChangeLines(affectedLines, true);
  297. break;
  298. default:
  299. break;
  300. }
  301. overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, -1, false);
  302. RedrawRightImage();
  303. }
  304. UpdateButtonStatus();*/
  305. }
  306. /// <summary>
  307. /// Detect Keyboard Shortcuts.
  308. /// </summary>
  309. private void Form1_KeyDown(object sender, KeyEventArgs e)
  310. {
  311. if (e.Modifiers == Keys.Control && e.KeyCode == Keys.Z)
  312. {
  313. ProgramPresenter.Undo();
  314. }
  315. if (e.Modifiers == Keys.Control && e.KeyCode == Keys.Y)
  316. {
  317. ProgramPresenter.Redo();
  318. }
  319. }
  320. /// <summary>
  321. /// Get current Mouse positon within the right picture box.
  322. /// </summary>
  323. private void pictureBoxRight_MouseMove(object sender, MouseEventArgs e)
  324. {
  325. ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Move, e);
  326. //currentCursorPosition = ConvertCoordinates(new Point(e.X, e.Y));
  327. }
  328. /// <summary>
  329. /// Hold left mouse button to start drawing.
  330. /// </summary>
  331. private void pictureBoxRight_MouseDown(object sender, MouseEventArgs e)
  332. {
  333. ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Down, e);
  334. /*
  335. mousePressed = true;
  336. if (currentState.Equals(ProgramState.Draw))
  337. {
  338. currentLine = new List<Point>();
  339. }*/
  340. }
  341. /// <summary>
  342. /// Lift left mouse button to stop drawing and add a new Line.
  343. /// </summary>
  344. private void pictureBoxRight_MouseUp(object sender, MouseEventArgs e)
  345. {
  346. ProgramPresenter.MouseEvent(MVP_Presenter.MouseAction.Up, e);
  347. /*
  348. mousePressed = false;
  349. if (currentState.Equals(ProgramState.Draw) && currentLine.Count > 0)
  350. {
  351. Line newLine = new Line(currentLine, rightLineList.Count);
  352. rightLineList.Add(new Tuple<bool, Line>(true, newLine));
  353. newLine.PopulateMatrixes(isFilledMatrix, linesMatrix);
  354. historyOfActions.AddNewAction(new SketchAction(SketchAction.ActionType.Draw, newLine.GetID()));
  355. //Execute a RedrawAssistant tick with the currently finished Line
  356. overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, newLine.GetID(), true);
  357. RedrawRightImage();
  358. }
  359. UpdateButtonStatus();*/
  360. }
  361. /// <summary>
  362. /// Button to create a new Canvas. Will create an empty image
  363. /// which is the size of the left image, if there is one.
  364. /// If there is no image loaded the canvas will be the size of the right picture box
  365. /// </summary>
  366. private void canvasButton_Click(object sender, EventArgs e)
  367. {
  368. ProgramPresenter.NewCanvas();
  369. /*
  370. if (CheckSavedStatus())
  371. {
  372. historyOfActions = new ActionHistory(lastActionTakenLabel);
  373. DrawEmptyCanvasRight();
  374. //The following lines cannot be in DrawEmptyCanvas()
  375. isFilledMatrix = new bool[rightImage.Width, rightImage.Height];
  376. linesMatrix = new HashSet<int>[rightImage.Width, rightImage.Height];
  377. rightLineList = new List<Tuple<bool, Line>>();
  378. //Reinitialise the Redraw Assistant.
  379. if(leftLineList != null)
  380. {
  381. redrawAss = new RedrawAssistant(leftLineList);
  382. UpdateSizes();
  383. overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, -1, false);
  384. RedrawRightImage();
  385. }
  386. }
  387. UpdateButtonStatus();
  388. UpdateSizes();*/
  389. }
  390. /// <summary>
  391. /// Add a Point on every tick to the Drawpath.
  392. /// Or detect lines for deletion on every tick
  393. /// </summary>
  394. private void mouseTimer_Tick(object sender, EventArgs e)
  395. {
  396. ProgramPresenter.Tick();
  397. /*
  398. if(cursorPositions.Count > 0) { previousCursorPosition = cursorPositions.Dequeue(); }
  399. else { previousCursorPosition = currentCursorPosition; }
  400. cursorPositions.Enqueue(currentCursorPosition);
  401. //Drawing
  402. if (currentState.Equals(ProgramState.Draw) && mousePressed)
  403. {
  404. rightGraph = Graphics.FromImage(rightImage);
  405. currentLine.Add(currentCursorPosition);
  406. Line drawline = new Line(currentLine);
  407. drawline.DrawLine(rightGraph);
  408. pictureBoxRight.Image = rightImage;
  409. //Redraw overlay gets ticked
  410. overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, rightLineList.Count, false);
  411. RedrawRightImage();
  412. }
  413. //Deleting
  414. if (currentState.Equals(ProgramState.Delete) && mousePressed)
  415. {
  416. List<Point> uncheckedPoints = GeometryCalculator.BresenhamLineAlgorithm(previousCursorPosition, currentCursorPosition);
  417. foreach (Point currPoint in uncheckedPoints)
  418. {
  419. HashSet<int> linesToDelete = CheckDeletionMatrixesAroundPoint(currPoint, deletionRadius);
  420. if (linesToDelete.Count > 0)
  421. {
  422. historyOfActions.AddNewAction(new SketchAction(SketchAction.ActionType.Delete, linesToDelete));
  423. foreach (int lineID in linesToDelete)
  424. {
  425. rightLineList[lineID] = new Tuple<bool, Line>(false, rightLineList[lineID].Item2);
  426. }
  427. RepopulateDeletionMatrixes();
  428. //Redraw overlay gets ticked
  429. overlayItems = redrawAss.Tick(currentCursorPosition, rightLineList, -1, false);
  430. RedrawRightImage();
  431. }
  432. }
  433. }*/
  434. }
  435. /*************************/
  436. /*** PRESENTER -> VIEW ***/
  437. /*************************/
  438. public void EnableTimer()
  439. {
  440. mouseTimer.Enabled = true;
  441. }
  442. /// <summary>
  443. /// A function that opens a file dialog and returns the filename.
  444. /// </summary>
  445. /// <param name="Filter">The filter that should be applied to the new Dialog.</param>
  446. /// <returns>Returns the FileName and the SafeFileName if the user correctly selects a file,
  447. /// else returns a tuple with empty strigns</returns>
  448. public Tuple<String, String> openNewDialog(String Filter)
  449. {
  450. openFileDialog.Filter = Filter;
  451. if (openFileDialog.ShowDialog() == DialogResult.OK)
  452. {
  453. return new Tuple<string, string>(openFileDialog.FileName, openFileDialog.SafeFileName);
  454. }
  455. else
  456. {
  457. return new Tuple<string, string>("", "");
  458. }
  459. }
  460. public void SetToolStripLoadStatus(String message)
  461. {
  462. toolStripLoadStatus.Text = message;
  463. }
  464. public void SetLastActionTakenText(String message)
  465. {
  466. lastActionTakenLabel.Text = message;
  467. }
  468. public void SetToolStripButtonStatus(String buttonName, ButtonState state)
  469. {
  470. ToolStripButton buttonToChange;
  471. switch (buttonName)
  472. {
  473. case "canvasButton":
  474. buttonToChange = canvasButton;
  475. break;
  476. case "drawButton":
  477. buttonToChange = drawButton;
  478. break;
  479. case "deleteButton":
  480. buttonToChange = deleteButton;
  481. break;
  482. case "undoButton":
  483. buttonToChange = undoButton;
  484. break;
  485. case "redoButton":
  486. buttonToChange = redoButton;
  487. break;
  488. default:
  489. Console.WriteLine("Invalid Button was given to SetToolStripButton. \nMaybe you forgot to add a case?");
  490. return;
  491. }
  492. switch (state)
  493. {
  494. case ButtonState.Active:
  495. buttonToChange.Checked = true;
  496. break;
  497. case ButtonState.Disabled:
  498. buttonToChange.Checked = false;
  499. buttonToChange.Enabled = false;
  500. break;
  501. case ButtonState.Enabled:
  502. buttonToChange.Checked = false;
  503. buttonToChange.Enabled = true;
  504. break;
  505. }
  506. }
  507. public void DisplayInLeftPictureBox(Image img)
  508. {
  509. pictureBoxLeft.Image = img;
  510. pictureBoxLeft.Refresh();
  511. }
  512. public void DisplayInRightPictureBox(Image img)
  513. {
  514. pictureBoxRight.Image = img;
  515. pictureBoxRight.Refresh();
  516. }
  517. /// <summary>
  518. /// shows the given info message in a popup and asks the user to aknowledge it
  519. /// </summary>
  520. /// <param name="message">the message to show</param>
  521. public void ShowInfoMessage(String message)
  522. {
  523. MessageBox.Show(message);
  524. }
  525. public bool ShowWarning(String message)
  526. {
  527. return (MessageBox.Show(message, "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes);
  528. }
  529. /***********************************/
  530. /*** HELPER FUNCTIONS START HERE ***/
  531. /***********************************/
  532. /// <summary>
  533. /// A function that returns a white canvas for a given width and height.
  534. /// </summary>
  535. /// <param name="width">The width of the canvas in pixels</param>
  536. /// <param name="height">The height of the canvas in pixels</param>
  537. /// <returns>The new canvas</returns>
  538. private Image GetEmptyCanvas(int width, int height)
  539. {
  540. Image image;
  541. try
  542. {
  543. image = new Bitmap(width, height);
  544. }
  545. catch(ArgumentException e)
  546. {
  547. ShowInfoMessage("The requested canvas size caused an error: \n" + e.ToString() + "\n The Canvas will be set to match your window.");
  548. image = new Bitmap(pictureBoxLeft.Width, pictureBoxLeft.Height);
  549. }
  550. Graphics graph = Graphics.FromImage(image);
  551. graph.FillRectangle(Brushes.White, 0, 0, width + 10, height + 10);
  552. return image;
  553. }
  554. /// <summary>
  555. /// Creates an empty Canvas
  556. /// </summary>
  557. private void DrawEmptyCanvasRight()
  558. {
  559. if (leftImage == null)
  560. {
  561. SetAndRefreshRightImage(GetEmptyCanvas(pictureBoxRight.Width, pictureBoxRight.Height));
  562. }
  563. else
  564. {
  565. SetAndRefreshRightImage(GetEmptyCanvas(leftImage.Width, leftImage.Height));
  566. }
  567. }
  568. /// <summary>
  569. /// Creates an empty Canvas on the left
  570. /// </summary>
  571. /// <param name="width"> width of the new canvas in pixels </param>
  572. /// <param name="height"> height of the new canvas in pixels </param>
  573. private void DrawEmptyCanvasLeft(int width, int height)
  574. {
  575. if (width == 0)
  576. {
  577. SetAndRefreshLeftImage(GetEmptyCanvas(pictureBoxLeft.Width, pictureBoxLeft.Height));
  578. }
  579. else
  580. {
  581. SetAndRefreshLeftImage(GetEmptyCanvas(width, height));
  582. }
  583. }
  584. /// <summary>
  585. /// Redraws all lines in lineList, for which their associated boolean value equals true.
  586. /// </summary>
  587. private void RedrawRightImage()
  588. {
  589. var workingCanvas = GetEmptyCanvas(rightImage.Width, rightImage.Height);
  590. var workingGraph = Graphics.FromImage(workingCanvas);
  591. //Lines
  592. foreach (Tuple<bool, Line> lineBoolTuple in rightLineList)
  593. {
  594. if (lineBoolTuple.Item1)
  595. {
  596. lineBoolTuple.Item2.DrawLine(workingGraph);
  597. }
  598. }
  599. //The Line being currently drawn
  600. if(currentLine != null && currentLine.Count > 0 && currentState.Equals(ProgramState.Draw) && mousePressed)
  601. {
  602. var currLine = new Line(currentLine);
  603. currLine.DrawLine(workingGraph);
  604. }
  605. //Overlay Items
  606. foreach (HashSet<Point> item in overlayItems)
  607. {
  608. foreach(Point p in item)
  609. {
  610. workingGraph.FillRectangle(Brushes.Green, p.X, p.Y, 1, 1);
  611. }
  612. }
  613. SetAndRefreshRightImage(workingCanvas);
  614. }
  615. /// <summary>
  616. /// A function to set rightImage and to refresh the respective PictureBox with it.
  617. /// </summary>
  618. /// <param name="image">The new Image</param>
  619. private void SetAndRefreshRightImage(Image image)
  620. {
  621. rightImage = image;
  622. pictureBoxRight.Image = rightImage;
  623. pictureBoxRight.Refresh();
  624. }
  625. /// <summary>
  626. /// A function to set leftImage and to refresh the respective PictureBox with it.
  627. /// </summary>
  628. /// <param name="image">The new Image</param>
  629. private void SetAndRefreshLeftImage(Image image)
  630. {
  631. leftImage = image;
  632. pictureBoxLeft.Image = leftImage;
  633. pictureBoxLeft.Refresh();
  634. }
  635. /// <summary>
  636. /// Change the status of whether or not the lines are shown.
  637. /// </summary>
  638. /// <param name="lines">The HashSet containing the affected Line IDs.</param>
  639. /// <param name="shown">True if the lines should be shown, false if they should be hidden.</param>
  640. private void ChangeLines(HashSet<int> lines, bool shown)
  641. {
  642. foreach (int lineId in lines)
  643. {
  644. if (lineId <= rightLineList.Count - 1 && lineId >= 0)
  645. {
  646. rightLineList[lineId] = new Tuple<bool, Line>(shown, rightLineList[lineId].Item2);
  647. }
  648. }
  649. RedrawRightImage();
  650. }
  651. /// <summary>
  652. /// Updates the active status of buttons. Currently draw, delete, undo and redo button.
  653. /// </summary>
  654. private void UpdateButtonStatus()
  655. {
  656. undoButton.Enabled = historyOfActions.CanUndo();
  657. redoButton.Enabled = historyOfActions.CanRedo();
  658. drawButton.Enabled = (rightImage != null);
  659. deleteButton.Enabled = (rightImage != null);
  660. }
  661. /// <summary>
  662. /// A helper function which handles tasks associated witch changing states,
  663. /// such as checking and unchecking buttons and changing the state.
  664. /// </summary>
  665. /// <param name="newState">The new state of the program</param>
  666. private void ChangeState(ProgramState newState)
  667. {
  668. switch (currentState)
  669. {
  670. case ProgramState.Draw:
  671. drawButton.CheckState = CheckState.Unchecked;
  672. mouseTimer.Enabled = false;
  673. break;
  674. case ProgramState.Delete:
  675. deleteButton.CheckState = CheckState.Unchecked;
  676. mouseTimer.Enabled = false;
  677. break;
  678. default:
  679. break;
  680. }
  681. switch (newState)
  682. {
  683. case ProgramState.Draw:
  684. drawButton.CheckState = CheckState.Checked;
  685. mouseTimer.Enabled = true;
  686. break;
  687. case ProgramState.Delete:
  688. deleteButton.CheckState = CheckState.Checked;
  689. mouseTimer.Enabled = true;
  690. break;
  691. default:
  692. break;
  693. }
  694. currentState = newState;
  695. pictureBoxRight.Refresh();
  696. }
  697. /// <summary>
  698. /// A function that calculates the coordinates of a point on a zoomed in image.
  699. /// </summary>
  700. /// <param name="">The position of the mouse cursor</param>
  701. /// <returns>The real coordinates of the mouse cursor on the image</returns>
  702. private Point ConvertCoordinates(Point cursorPosition)
  703. {
  704. Point realCoordinates = new Point(5,3);
  705. if(pictureBoxRight.Image == null)
  706. {
  707. return cursorPosition;
  708. }
  709. int widthImage = pictureBoxRight.Image.Width;
  710. int heightImage = pictureBoxRight.Image.Height;
  711. int widthBox = pictureBoxRight.Width;
  712. int heightBox = pictureBoxRight.Height;
  713. float imageRatio = (float)widthImage / (float)heightImage;
  714. float containerRatio = (float)widthBox / (float)heightBox;
  715. if (imageRatio >= containerRatio)
  716. {
  717. //Image is wider than it is high
  718. float zoomFactor = (float)widthImage / (float)widthBox;
  719. float scaledHeight = heightImage / zoomFactor;
  720. float filler = (heightBox - scaledHeight) / 2;
  721. realCoordinates.X = (int)(cursorPosition.X * zoomFactor);
  722. realCoordinates.Y = (int)((cursorPosition.Y - filler) * zoomFactor);
  723. }
  724. else
  725. {
  726. //Image is higher than it is wide
  727. float zoomFactor = (float)heightImage / (float)heightBox;
  728. float scaledWidth = widthImage / zoomFactor;
  729. float filler = (widthBox - scaledWidth) / 2;
  730. realCoordinates.X = (int)((cursorPosition.X - filler) * zoomFactor);
  731. realCoordinates.Y = (int)(cursorPosition.Y * zoomFactor);
  732. }
  733. return realCoordinates;
  734. }
  735. /// <summary>
  736. /// A function that populates the matrixes needed for deletion detection with line data.
  737. /// </summary>
  738. private void RepopulateDeletionMatrixes()
  739. {
  740. if(rightImage != null)
  741. {
  742. isFilledMatrix = new bool[rightImage.Width,rightImage.Height];
  743. linesMatrix = new HashSet<int>[rightImage.Width, rightImage.Height];
  744. foreach(Tuple<bool,Line> lineTuple in rightLineList)
  745. {
  746. if (lineTuple.Item1)
  747. {
  748. lineTuple.Item2.PopulateMatrixes(isFilledMatrix, linesMatrix);
  749. }
  750. }
  751. }
  752. }
  753. /// <summary>
  754. /// A function that checks the deletion matrixes at a certain point
  755. /// and returns all Line ids at that point and in a square around it in a certain range.
  756. /// </summary>
  757. /// <param name="p">The point around which to check.</param>
  758. /// <param name="range">The range around the point. If range is 0, only the point is checked.</param>
  759. /// <returns>A List of all lines.</returns>
  760. private HashSet<int> CheckDeletionMatrixesAroundPoint(Point p, int range)
  761. {
  762. HashSet<int> returnSet = new HashSet<int>();
  763. foreach(Point pnt in GeometryCalculator.FilledCircleAlgorithm(p, (int)range))
  764. {
  765. if(pnt.X >= 0 && pnt.Y >= 0 && pnt.X < rightImage.Width && pnt.Y < rightImage.Height)
  766. {
  767. if (isFilledMatrix[pnt.X, pnt.Y])
  768. {
  769. returnSet.UnionWith(linesMatrix[pnt.X, pnt.Y]);
  770. }
  771. }
  772. }
  773. return returnSet;
  774. }
  775. /// <summary>
  776. /// binds the given picture to templatePicture and draws it
  777. /// </summary>
  778. /// <param name="newTemplatePicture"> the new template picture, represented as a list of polylines </param>
  779. /// <returns></returns>
  780. private void BindAndDrawLeftImage(List<Line> newTemplatePicture)
  781. {
  782. leftLineList = newTemplatePicture;
  783. foreach(Line l in leftLineList)
  784. {
  785. l.DrawLine(Graphics.FromImage(leftImage));
  786. }
  787. }
  788. /// <summary>
  789. /// Will calculate the start and endpoints of the given line on the right canvas.
  790. /// </summary>
  791. /// <param name="line">The line.</param>
  792. /// <param name="size">The size of the circle with which the endpoints of the line are marked.</param>
  793. private Tuple<HashSet<Point>, HashSet<Point>> CalculateStartAndEnd(Line line, int size)
  794. {
  795. var circle0 = GeometryCalculator.FilledCircleAlgorithm(line.GetStartPoint(), size);
  796. var circle1 = GeometryCalculator.FilledCircleAlgorithm(line.GetEndPoint(), size);
  797. var currentLineEndings = new Tuple<HashSet<Point>, HashSet<Point>>(circle0, circle1);
  798. return currentLineEndings;
  799. }
  800. /// <summary>
  801. /// A helper Function that updates the markerRadius & deletionRadius, considering the size of the canvas.
  802. /// </summary>
  803. private void UpdateSizes()
  804. {
  805. if (rightImage != null)
  806. {
  807. int widthImage = pictureBoxRight.Image.Width;
  808. int heightImage = pictureBoxRight.Image.Height;
  809. int widthBox = pictureBoxRight.Width;
  810. int heightBox = pictureBoxRight.Height;
  811. float imageRatio = (float)widthImage / (float)heightImage;
  812. float containerRatio = (float)widthBox / (float)heightBox;
  813. float zoomFactor = 0;
  814. if (imageRatio >= containerRatio)
  815. {
  816. //Image is wider than it is high
  817. zoomFactor = (float)widthImage / (float)widthBox;
  818. }
  819. else
  820. {
  821. //Image is higher than it is wide
  822. zoomFactor = (float)heightImage / (float)heightBox;
  823. }
  824. markerRadius = (int)(10 * zoomFactor);
  825. redrawAss.SetMarkerRadius(markerRadius);
  826. deletionRadius = (int)(5 * zoomFactor);
  827. }
  828. }
  829. /// <summary>
  830. /// Checks if there is unsaved progess, and warns the user. Returns True if it safe to continue.
  831. /// </summary>
  832. /// <returns>true if there is none, or the user wishes to continue without saving.
  833. /// false if there is progress, and the user doesn't wish to continue.</returns>
  834. private bool CheckSavedStatus()
  835. {
  836. if (!historyOfActions.IsEmpty())
  837. {
  838. return (MessageBox.Show("You have unsaved changes, do you wish to continue?",
  839. "Attention", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes);
  840. }
  841. return true;
  842. }
  843. /********************************************/
  844. /*** TESTING RELATED FUNCTIONS START HERE ***/
  845. /********************************************/
  846. /// <summary>
  847. /// returns all instance variables in the order of their definition for testing
  848. /// </summary>
  849. /// <returns>A list of tuples containing names of variables and the variable themselves.
  850. /// Cast according to the Type definitions in the class variable section.</returns>
  851. public List<Tuple<String, Object>>GetAllVariables()
  852. {
  853. var objArr = new (String, object)[] { ("currentState", currentState), ("fileImporter", fileImporter), ("openFileDialog", openFileDialog),
  854. ("leftImage", leftImage), ("leftLineList", leftLineList), ("rightImage", rightImage), ("currentLine", currentLine),
  855. ("rightLineList", rightLineList), ("mousePressed", mousePressed), ("currentCursorPosition", currentCursorPosition),
  856. ("previousCursorPosition", previousCursorPosition), ("cursorPositions", cursorPositions), ("rightGraph", rightGraph),
  857. ("isFilledMatrix", isFilledMatrix), ("linesMatrix", linesMatrix), ("deletionRadius", deletionRadius),
  858. ("historyOfActions", historyOfActions), ("overlayItems", overlayItems), ("redrawAss", redrawAss), ("markerRadius", markerRadius) };
  859. var varArr = new List<Tuple<String, Object>>();
  860. foreach((String, object) obj in objArr)
  861. {
  862. varArr.Add(new Tuple<string, object>(obj.Item1, obj.Item2));
  863. }
  864. return varArr;
  865. }
  866. /// <summary>
  867. /// public method wrapper for testing purposes, invoking DrawEmptyCanvas(...) and BindAndDrawLeftImage(...)
  868. /// </summary>
  869. /// <param name="width">width of the parsed image</param>
  870. /// <param name="height">height of the parsed image</param>
  871. /// <param name="newImage">the parsed image</param>
  872. public void CreateCanvasAndSetPictureForTesting(int width, int height, List<Line> newImage)
  873. {
  874. DrawEmptyCanvasLeft(width, height);
  875. BindAndDrawLeftImage(newImage);
  876. }
  877. }
  878. }