Form1.cs 32 KB

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