FileImporter.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.Globalization;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Text.RegularExpressions;
  8. using System.Threading.Tasks;
  9. namespace SketchAssistant
  10. {
  11. public class FileImporter
  12. {
  13. /// <summary>
  14. /// pointer to the running instance of main program
  15. /// </summary>
  16. Form1 program;
  17. /// <summary>
  18. /// scale factor for coordinates of svg file
  19. /// </summary>
  20. double scale;
  21. /// <summary>
  22. /// line pointer for the current svg document
  23. /// </summary>
  24. int i;
  25. /// <summary>
  26. /// array containing all characters interpreted as whitespaces which seperate words/tokens in the input file
  27. /// </summary>
  28. readonly char[] whitespaces = new char[] { ' ' , ',' };
  29. /// <summary>
  30. /// number of points to create along the outline of an ellipse, divisible by 4
  31. /// </summary>
  32. readonly int samplingRateEllipse = 24;
  33. public FileImporter(Form1 newProgram)
  34. {
  35. program = newProgram;
  36. }
  37. /// <summary>
  38. /// parses a drawing consisting of line objects, given as a file in the application specific .isad format
  39. /// </summary>
  40. /// <param name="fileName">the path of the input file</param>
  41. /// <returns>the width and height of the left canvas and the parsed picture as a list of lines</returns>
  42. public (int, int, List<Line>) ParseISADInputFile(String fileName)
  43. {
  44. return ParseISADInput(System.IO.File.ReadAllLines(fileName));
  45. }
  46. /// <summary>
  47. /// parses a drawing consisting of line objects, given as the content of a .isad file, seperated into lines
  48. /// </summary>
  49. /// <param name="allLines">an array holding all lines of the input file</param>
  50. /// <returns>the width and height of the left canvas and the parsed picture as a list of lines</returns>
  51. private (int, int, List<Line>) ParseISADInput(String[] allLines)
  52. {
  53. if (allLines.Length == 0)
  54. {
  55. throw new FileImporterException("file is empty", "", -1);
  56. }
  57. if (!"drawing".Equals(allLines[0]))
  58. {
  59. throw new FileImporterException("file is not an interactive sketch assistant drawing", ".isad files have to start with the 'drawing' token", 1);
  60. }
  61. if (!"enddrawing".Equals(allLines[allLines.Length - 1]))
  62. {
  63. throw new FileImporterException("unterminated drawing definition", ".isad files have to end with the 'enddrawing' token", allLines.Length);
  64. }
  65. (int, int) dimensions = ParseISADHeader(allLines);
  66. List<Line> picture = ParseISADBody(allLines, dimensions.Item1, dimensions.Item2);
  67. return (dimensions.Item1, dimensions.Item2, picture);
  68. }
  69. /// <summary>
  70. /// parses the first two lines of an input file in .isad format
  71. /// </summary>
  72. /// <param name="allLines">the input file as an array of lines</param>
  73. /// <returns>the width and height of the left canvas</returns>
  74. private (int, int) ParseISADHeader(String[] allLines)
  75. {
  76. int width;
  77. int height;
  78. if (!(allLines.Length > 1) || !Regex.Match(allLines[1], @"^\d+x?\d+$", RegexOptions.None).Success)
  79. {
  80. throw new FileImporterException("invalid or missing canvas size definition", "format: [width]x[heigth]", 2);
  81. }
  82. String[] size = allLines[1].Split('x');
  83. width = Convert.ToInt32(size[0]);
  84. height = Convert.ToInt32(size[1]);
  85. return (width, height);
  86. }
  87. /// <summary>
  88. /// parses all line entries of an input file in .isad format
  89. /// </summary>
  90. /// <param name="allLines">the input file as an array of lines</param>
  91. /// <returns>the parsed picture as a list of lines</returns>
  92. private List<Line> ParseISADBody(String[] allLines, int width, int height)
  93. {
  94. String lineStartString = "line";
  95. String lineEndString = "endline";
  96. List<Line> drawing = new List<Line>();
  97. //number of the line currently being parsed, enumeration starting at 0, body starts at the third line, therefore lin number 2
  98. int i = 2;
  99. //parse 'line' token and complete line definition
  100. int lineStartPointer = i;
  101. //holds the line number of the next expected beginning of a line definition, or of the enddrawing token
  102. while (lineStartString.Equals(allLines[i]))
  103. {
  104. //start parsing next line
  105. i++;
  106. List<Point> newLine = new List<Point>();
  107. while (!lineEndString.Equals(allLines[i]))
  108. {
  109. if (i == allLines.Length)
  110. {
  111. throw new FileImporterException("unterminated line definition", null, (i + 1));
  112. }
  113. //parse single point definition
  114. if (!Regex.Match(allLines[i], @"^\d+;\d+$", RegexOptions.None).Success)
  115. {
  116. throw new FileImporterException("invalid Point definition: wrong format", "format: [xCoordinate];[yCoordinate]", (i + 1) );
  117. }
  118. String[] coordinates = allLines[i].Split(';');
  119. //no errors possible, convertability to int already checked above
  120. int xCoordinate = Convert.ToInt32(coordinates[0]);
  121. int yCoordinate = Convert.ToInt32(coordinates[1]);
  122. if (xCoordinate < 0 || yCoordinate < 0 || xCoordinate > width - 1 || yCoordinate > height - 1)
  123. {
  124. throw new FileImporterException("invalid Point definition: point out of bounds", null, (i + 1) );
  125. }
  126. newLine.Add(new Point(xCoordinate, yCoordinate));
  127. //start parsing next line
  128. i++;
  129. }
  130. //"parse" 'endline' token, syntax already checked at the beginning, and start parsing next line
  131. i++;
  132. //add line to drawing
  133. drawing.Add(new Line(newLine));
  134. //update lineStartPointer to the presumable start of the next line
  135. lineStartPointer = i;
  136. }
  137. //check if end of body is reached after there are no more line definitions
  138. if(i != allLines.Length - 1)
  139. {
  140. throw new FileImporterException("missing or invalid line definition token", "line definitions start with the 'line' token", (i + 1));
  141. }
  142. //return parsed picture
  143. return drawing;
  144. }
  145. /// <summary>
  146. /// connection point for testing use only: calls ParseISADInput(String[] allLines) and directly passes the given argument (effectively bypassing the File Input functionality)
  147. /// </summary>
  148. /// <param name="allLines">an array holding all lines of the input file</param>
  149. /// <returns>the width and height of the left canvas and the parsed picture as a list of lines</returns>
  150. public (int, int, List<Line>) ParseISADInputForTesting(String[] allLines)
  151. {
  152. return ParseISADInput(allLines);
  153. }
  154. /// <summary>
  155. /// parses a svg drawing, given as a .svg file
  156. /// </summary>
  157. /// <param name="fileName">the path of the input file</param>
  158. /// <returns>the width and height of the left canvas and the parsed picture as a list of lines</returns>
  159. public (int, int, List<Line>) ParseSVGInputFile(String fileName, int windowWidth, int windowHeight)
  160. {
  161. return ParseSVGInput(System.IO.File.ReadAllLines(fileName), windowWidth, windowHeight);
  162. }
  163. /// <summary>
  164. /// parses a svg drawing, given as the content of a .svg file, seperated into lines
  165. /// </summary>
  166. /// <param name="allLines">an array holding all lines of the input file</param>
  167. /// <returns>the width and height of the left canvas and the parsed picture as a list of lines</returns>
  168. private (int, int, List<Line>) ParseSVGInput(String[] allLines, double windowWidth, double windowHeight)
  169. {
  170. i = 0; //reset line pointer
  171. if (allLines.Length == 0) //check for empty file
  172. {
  173. throw new FileImporterException("file is empty", "", -1);
  174. }
  175. (int, int) sizedef = ParseSVGHeader(allLines); //parse svg file header and get internal coordinate range
  176. i++;
  177. int width; //width of the resulting picture in pixels
  178. int height; //height of the resulting picture in pixels
  179. if (windowWidth / windowHeight > sizedef.Item1 / sizedef.Item2) //height dominant, width has to be smaller than drawing window to preserve xy-scale
  180. {
  181. scale = windowHeight / sizedef.Item2;
  182. Console.WriteLine("scale: (heights) " + windowHeight + "/" + sizedef.Item2);
  183. Console.WriteLine("widths: " + windowWidth + "/" + sizedef.Item1);
  184. height = (int)Math.Round(windowHeight);
  185. width = (int) Math.Round(scale * sizedef.Item1);
  186. Console.WriteLine(width + "x" + height + " (" + scale + ")");
  187. }
  188. else //width dominant, height has to be smaller than drawing window to preserve xy-scale
  189. {
  190. scale = windowWidth / sizedef.Item1;
  191. Console.WriteLine("scale: (widths) " + windowWidth + "/" + sizedef.Item1);
  192. Console.WriteLine("heights: " + windowHeight + "/" + sizedef.Item2);
  193. width = (int)Math.Round(windowWidth);
  194. height = (int)Math.Round(scale * sizedef.Item2);
  195. Console.WriteLine(width + "x" + height + " (" + scale + ")");
  196. }
  197. for(int j=0; j < allLines.Length; j++)
  198. {
  199. allLines[j] = allLines[j].Trim(whitespaces);
  200. }
  201. List<Line> picture = ParseSVGBody(allLines); //parse whole svg drawing into list of lines
  202. return (width, height, picture);
  203. }
  204. /// <summary>
  205. /// parses the svg file header and returns the internal coordinate range of this drawing, and iterates i to point to the start of svg element definitions
  206. /// </summary>
  207. /// <param name="allLines">an array holding all lines of the input file</param>
  208. /// <returns>the internal coordinate range of this drawing</returns>
  209. private (int, int) ParseSVGHeader(String[] allLines)
  210. {
  211. while (!allLines[i].StartsWith("<svg")) //skip non-relevant metadata at start of svg file
  212. {
  213. i++;
  214. }
  215. String[] currentLine = allLines[i].Split(' ');
  216. int width= -1;
  217. int height= -1;
  218. for(int j= 0; j < currentLine.Length; j++)
  219. {
  220. if (currentLine[j].StartsWith("width"))
  221. {
  222. width = Convert.ToInt32(ParseSingleSVGAttribute(currentLine[j]));
  223. }
  224. else if (currentLine[j].StartsWith("height"))
  225. {
  226. height = Convert.ToInt32(ParseSingleSVGAttribute(currentLine[j]));
  227. }
  228. }
  229. if(width == -1)
  230. {
  231. throw new FileImporterException("missing width definition in SVG header", "the header should contain the \"width=...\" attribute", i+1);
  232. }
  233. if (height == -1)
  234. {
  235. throw new FileImporterException("missing height definition in SVG header", "the header should contain the \"height=...\" attribute", i + 1);
  236. }
  237. return (width, height);
  238. }
  239. /// <summary>
  240. /// parses all relevant svg element definitions and skips the ones not representable by the sketch assistant
  241. /// </summary>
  242. /// <param name="allLines">an array holding all lines of the input file</param>
  243. /// <returns>the parsed picture as a list of lines</returns>
  244. private List<Line> ParseSVGBody(String[] allLines)
  245. {
  246. List<Line> picture = new List<Line>();
  247. while (!allLines[i].StartsWith("</svg"))
  248. {
  249. List<Line> element = ParseSingleSVGElement(allLines);
  250. if (element != null)
  251. {
  252. picture.AddRange(element);
  253. }
  254. i++;
  255. }
  256. return picture;
  257. }
  258. /// <summary>
  259. /// parses one toplevel svg element
  260. /// </summary>
  261. /// <param name="allLines">an array holding all lines of the input file</param>
  262. /// <returns>the parsed Element as a list of lines</returns>
  263. private List<Line> ParseSingleSVGElement(string[] allLines)
  264. {
  265. String[] currentElement = GetCurrentElement(allLines);
  266. if (currentElement[currentElement.Length - 1].EndsWith("/>")) //single line element
  267. {
  268. return ParseSingleLineSVGElement(currentElement);
  269. }
  270. else //element containing sub-elements
  271. {
  272. return ParseMultiLineSVGElement(currentElement, allLines);
  273. }
  274. }
  275. /// <summary>
  276. /// parses a single toplevel svg element only taking one line
  277. /// </summary>
  278. /// <param name="allLines">an array holding all lines of the input file</param>
  279. /// <returns>the parsed element as a Line object, or null if the element is not supported</returns>
  280. private List<Line> ParseSingleLineSVGElement(string[] currentElement)
  281. {
  282. List<Point> points= null;
  283. List<Line> element = null;
  284. switch (currentElement[0])
  285. {
  286. case "<rect":
  287. points = parseRect(currentElement);
  288. break;
  289. case "<circle":
  290. points = parseCircle(currentElement);
  291. break;
  292. case "<ellipse":
  293. points = parseEllipse(currentElement);
  294. break;
  295. case "<line":
  296. points = parseLine(currentElement);
  297. break;
  298. case "<polyline":
  299. points = parsePolyline(currentElement);
  300. break;
  301. case "<polygon":
  302. points = parsePolygon(currentElement);
  303. break;
  304. case "<path":
  305. element = parsePath(currentElement);
  306. break;
  307. default: //unsupported svg element
  308. Console.WriteLine("unsupported element: " + currentElement[0] + currentElement[0].Length);
  309. return null;
  310. }
  311. if (element == null)
  312. {
  313. element = new List<Line>();
  314. element.Add(new Line(points));
  315. }
  316. return element;
  317. }
  318. /// <summary>
  319. /// parses a rectangle definition into a List of Points representing a single line around the rectangle (in clockwise direction)
  320. /// </summary>
  321. /// <param name="currentElement">the definition of the element as whitespace seperated String[]</param>
  322. /// <returns>the parsed element as a List of Points</returns>
  323. private List<Point> parseRect(string[] currentElement)
  324. {
  325. double x = 0;
  326. double y = 0;
  327. double w = 0;
  328. double h = 0;
  329. double rx = 0;
  330. double ry = 0;
  331. for(int j= 0; j < currentElement.Length; j++)
  332. {
  333. if (currentElement[j].StartsWith("x="))
  334. {
  335. x = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  336. }
  337. else if (currentElement[j].StartsWith("y="))
  338. {
  339. y = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  340. }
  341. else if (currentElement[j].StartsWith("width="))
  342. {
  343. w = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  344. }
  345. else if (currentElement[j].StartsWith("height="))
  346. {
  347. h = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  348. }
  349. else if (currentElement[j].StartsWith("rx="))
  350. {
  351. rx = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  352. }
  353. else if (currentElement[j].StartsWith("ry="))
  354. {
  355. ry = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  356. }
  357. }
  358. List<Point> rect = new List<Point>();
  359. rect.Add(ScaleAndCreatePoint(x, y));
  360. rect.Add(ScaleAndCreatePoint(x + w, y));
  361. rect.Add(ScaleAndCreatePoint(x + w, y + h));
  362. rect.Add(ScaleAndCreatePoint(x, y + h));
  363. rect.Add(ScaleAndCreatePoint(x, y));
  364. Console.WriteLine("parsed point: " + x + ";" + y);
  365. return rect;
  366. }
  367. /// <summary>
  368. /// parses a circle definition into a List of Points
  369. /// </summary>
  370. /// <param name="currentElement">the definition of the element as whitespace seperated String[]</param>
  371. /// <returns>the parsed element as a List of Points</returns>
  372. private List<Point> parseCircle(string[] currentElement)
  373. {
  374. double x = 0;
  375. double y = 0;
  376. double r = 0;
  377. for (int j = 0; j < currentElement.Length; j++)
  378. {
  379. if (currentElement[j].StartsWith("cx="))
  380. {
  381. x = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  382. }
  383. else if (currentElement[j].StartsWith("cy="))
  384. {
  385. y = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  386. }
  387. else if (currentElement[j].StartsWith("r="))
  388. {
  389. r = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  390. }
  391. }
  392. return SampleEllipse(x, y, r, r);
  393. }
  394. /// <summary>
  395. /// parses a ellipse definition into a List of Points
  396. /// </summary>
  397. /// <param name="currentElement">the definition of the element as whitespace seperated String[]</param>
  398. /// <returns>the parsed element as a List of Points</returns>
  399. private List<Point> parseEllipse(string[] currentElement)
  400. {
  401. double x = 0;
  402. double y = 0;
  403. double rx = 0;
  404. double ry = 0;
  405. for (int j = 0; j < currentElement.Length; j++)
  406. {
  407. if (currentElement[j].StartsWith("cx="))
  408. {
  409. x = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  410. }
  411. else if (currentElement[j].StartsWith("cy="))
  412. {
  413. y = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  414. }
  415. else if (currentElement[j].StartsWith("rx="))
  416. {
  417. rx = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  418. }
  419. else if (currentElement[j].StartsWith("ry="))
  420. {
  421. ry = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  422. }
  423. }
  424. return SampleEllipse(x, y, rx, ry);
  425. }
  426. /// <summary>
  427. /// parses a line definition into a List of two Points
  428. /// </summary>
  429. /// <param name="currentElement">the definition of the element as whitespace seperated String[]</param>
  430. /// <returns>the parsed element as a List of Points</returns>
  431. private List<Point> parseLine(string[] currentElement)
  432. {
  433. double x1 = 0;
  434. double y1 = 0;
  435. double x2 = 0;
  436. double y2 = 0;
  437. for (int j = 0; j < currentElement.Length; j++)
  438. {
  439. if (currentElement[j].StartsWith("x1="))
  440. {
  441. x1 = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  442. }
  443. else if (currentElement[j].StartsWith("y1="))
  444. {
  445. y1 = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  446. }
  447. else if (currentElement[j].StartsWith("x2="))
  448. {
  449. x2 = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  450. }
  451. else if (currentElement[j].StartsWith("y2="))
  452. {
  453. y2 = Convert.ToDouble(ParseSingleSVGAttribute(currentElement[j]), CultureInfo.InvariantCulture);
  454. }
  455. }
  456. List<Point> line = new List<Point>();
  457. line.Add(ScaleAndCreatePoint(x1, y1));
  458. line.Add(ScaleAndCreatePoint(x2, y2));
  459. return line;
  460. }
  461. /// <summary>
  462. /// parses a polyline definition into a List of Points
  463. /// </summary>
  464. /// <param name="currentElement">the definition of the element as whitespace seperated String[]</param>
  465. /// <returns>the parsed element as a List of Points</returns>
  466. private List<Point> parsePolyline(string[] currentElement)
  467. {
  468. String[] points = null;
  469. for (int j = 0; j < currentElement.Length; j++)
  470. {
  471. if (currentElement[j].StartsWith("points="))
  472. {
  473. List<String> pointDefs = new List<string>();
  474. pointDefs.Add(currentElement[j].Substring(8)); //parse first point coordinates by removing 'points="'
  475. j++;
  476. while (!currentElement[j].EndsWith("\""))
  477. {
  478. pointDefs.Add(currentElement[j]); //parse intermediate point coordinates
  479. j++;
  480. }
  481. pointDefs.Add(currentElement[j].Trim('"')); //parse last point coordinates by removing '"'
  482. points = pointDefs.ToArray();
  483. }
  484. }
  485. List<Point> polyline = new List<Point>();
  486. for (int k = 0; k < points.Length - 1; k += 2)
  487. {
  488. polyline.Add(ScaleAndCreatePoint(Convert.ToDouble(points[k], CultureInfo.InvariantCulture), Convert.ToDouble(points[k + 1], CultureInfo.InvariantCulture)));
  489. }
  490. return polyline;
  491. }
  492. /// <summary>
  493. /// parses a polygon definition into a List of Points
  494. /// </summary>
  495. /// <param name="currentElement">the definition of the element as whitespace seperated String[]</param>
  496. /// <returns>the parsed element as a List of Points</returns>
  497. private List<Point> parsePolygon(string[] currentElement)
  498. {
  499. String[] points = null;
  500. for (int j = 0; j < currentElement.Length; j++)
  501. {
  502. if (currentElement[j].StartsWith("points="))
  503. {
  504. List<String> pointDefs = new List<string>();
  505. pointDefs.Add(currentElement[j].Substring(8)); //parse first point coordinates by removing 'points="'
  506. j++;
  507. while (!currentElement[j].EndsWith("\""))
  508. {
  509. pointDefs.Add(currentElement[j]); //parse intermediate point coordinates
  510. j++;
  511. }
  512. pointDefs.Add(currentElement[j].Trim('"')); //parse last point coordinates by removing '"'
  513. points = pointDefs.ToArray();
  514. }
  515. }
  516. List<Point> polygon = new List<Point>();
  517. for (int k = 0; k < points.Length - 1; k+=2)
  518. {
  519. polygon.Add(ScaleAndCreatePoint(Convert.ToDouble(points[k], CultureInfo.InvariantCulture), Convert.ToDouble(points[k+1], CultureInfo.InvariantCulture)));
  520. Console.WriteLine("parsed point: " + points[k] + ";" + points[k + 1]);
  521. }
  522. polygon.Add(ScaleAndCreatePoint(Convert.ToDouble(points[0], CultureInfo.InvariantCulture), Convert.ToDouble(points[1], CultureInfo.InvariantCulture))); //close polygon
  523. Console.WriteLine("parsed point: " + points[0] + ";" + points[1]);
  524. return polygon;
  525. }
  526. /// <summary>
  527. /// parses a path definition into a List of Points
  528. /// </summary>
  529. /// <param name="currentElement">the definition of the element as whitespace seperated String[]</param>
  530. /// <returns>the parsed element as a List of Points</returns>
  531. private List<Line> parsePath(string[] currentElement)
  532. {
  533. List<String> pathElements = new List<string>();
  534. /*
  535. for (int j = 0; j < currentElement.Length; j++)
  536. {
  537. if (currentElement[j].StartsWith("d="))
  538. {
  539. pathElements.Add(currentElement[j].Substring(3)); //parse first path element by removing 'd="'
  540. j++;
  541. while (!currentElement[j].EndsWith("\""))
  542. {
  543. pathElements.Add(currentElement[j]); //parse intermediate path element
  544. j++;
  545. }
  546. pathElements.Add(currentElement[j].Trim('"')); //parse last path element by removing '"'
  547. }
  548. }
  549. List<Line> element = new List<Line>();
  550. List<Point> currentLine = new List<Point>();
  551. Point mirroredBezierPoint;
  552. pathElements = PreparePathElements(pathElements); //split pathElement list objects until every object is atomar (single character or single number (coordinate))
  553. //int k = 0; //index of active element in pathElements is always 0
  554. currentLine = parse_M(pathElements);
  555. while(!(pathElements.Count == 0){
  556. if (pathElements.First().Equals("M"))
  557. {
  558. element.Add(new Line(currentLine));
  559. currentLine = parse_M(pathElements);
  560. }
  561. else if (pathElements.First().Equals(""))
  562. {
  563. }
  564. }
  565. return element;
  566. */
  567. return null;
  568. }
  569. /// <summary>
  570. /// parses a hierarchical svg element and all its sub-elements
  571. /// </summary>
  572. /// <param name="currentElement">the definition of the top level element as whitespace seperated String[]</param>
  573. /// <param name="allLines">an array holding all lines of the input file</param>
  574. /// <returns>the parsed element as a Line object, or null if the element is not supported</returns>
  575. private List<Line> ParseMultiLineSVGElement(string[] currentElement, string[] allLines)
  576. {
  577. throw new NotImplementedException();
  578. }
  579. /// <summary>
  580. /// removes the name of the attribute aswell as the '="' at the beginning and the '"' or '">' at the end of an attribute definition
  581. /// </summary>
  582. /// <param name="definition">the definition from the svg file</param>
  583. /// <returns>the value of the attribute, as String (the part of definition contained between '"'s)</returns>
  584. private String ParseSingleSVGAttribute(String definition)
  585. {
  586. return definition.Split('"')[1];
  587. }
  588. /// <summary>
  589. /// fetches a single svg element definition that may extend ovr several lines of the input file, iterates i to point to the last line of the element definition
  590. /// </summary>
  591. /// <param name="allLines">an array holding all lines of the input file</param>
  592. /// <returns>the definition of the current svg element, as String[] split by whitespaces</returns>
  593. private String[] GetCurrentElement(String[] allLines)
  594. {
  595. List<String> currentElementTemp = allLines[i].Split(whitespaces).ToList();
  596. while (!currentElementTemp.Last().EndsWith(">"))
  597. {
  598. i++;
  599. currentElementTemp.AddRange(allLines[i].Split(whitespaces).ToList());
  600. }
  601. return currentElementTemp.ToArray();
  602. }
  603. /// <summary>
  604. /// applies the scale factor to the coordinates and creates a new Point
  605. /// </summary>
  606. /// <param name="x">unscaled x coordinate</param>
  607. /// <param name="y">unscaled y coordinate</param>
  608. /// <returns>new Point with scaled coordinates</returns>
  609. private Point ScaleAndCreatePoint(double x, double y)
  610. {
  611. return new Point((int)Math.Round(x * scale), (int)Math.Round(y * scale));
  612. }
  613. /// <summary>
  614. /// creates a representation of an ellipse as a List of Points by sampling the outline of the ellipse
  615. /// </summary>
  616. /// <param name="x">x coordinate of the center of the ellipse</param>
  617. /// <param name="y">y coordinate of the center of the ellipse</param>
  618. /// <param name="rx">x radius of the ellipse</param>
  619. /// <param name="ry">y radius of the ellipse</param>
  620. /// <returns>the parsed element as a List of Points</returns>
  621. private List<Point> SampleEllipse(double x, double y, double rx, double ry)
  622. {
  623. List<Point> ellipse = new List<Point>();
  624. double angle = ((double)2 * Math.PI) / (double)samplingRateEllipse;
  625. double yScale = ry / rx;
  626. Console.WriteLine("parsing ellipse: " + x + ";" + y + "(" + rx + "x" + ry + ")" + " " + yScale + ":" + angle);
  627. double[] xValues = new double[samplingRateEllipse / 4];
  628. double[] yValues = new double[samplingRateEllipse / 4];
  629. for (int j = 0; j < samplingRateEllipse / 4; j++) //compute offset values of points for one quadrant
  630. {
  631. xValues[j] = Math.Sin((double)j * angle) * rx;
  632. yValues[j] = Math.Cos((double)j * angle) * rx;
  633. Console.WriteLine("parsed ellipse value: " + xValues[j] + ";" + yValues[j]);
  634. }
  635. for (int j = 0; j < samplingRateEllipse / 4; j++) //create actual points for first quadrant
  636. {
  637. int xCoord = Convert.ToInt32(Math.Round(x + xValues[j]));
  638. int yCoord = Convert.ToInt32(Math.Round(y - yValues[j] * yScale));
  639. ellipse.Add(ScaleAndCreatePoint(xCoord, yCoord));
  640. Console.WriteLine("parsed ellipse point: " + xCoord + ";" + yCoord + " pointCount: " + (samplingRateEllipse / 4));
  641. }
  642. for (int j = 0; j < samplingRateEllipse / 4; j++) //create actual points for second quadrant
  643. {
  644. int xCoord = Convert.ToInt32(Math.Round(x + yValues[j]));
  645. int yCoord = Convert.ToInt32(Math.Round(y + xValues[j] * yScale));
  646. ellipse.Add(ScaleAndCreatePoint(xCoord, yCoord));
  647. Console.WriteLine("parsed ellipse point: " + xCoord + ";" + yCoord + " pointCount: " + (samplingRateEllipse / 4));
  648. }
  649. for (int j = 0; j < samplingRateEllipse / 4; j++) //create actual points for third quadrant
  650. {
  651. int xCoord = Convert.ToInt32(Math.Round(x - xValues[j]));
  652. int yCoord = Convert.ToInt32(Math.Round(y + yValues[j] * yScale));
  653. ellipse.Add(ScaleAndCreatePoint(xCoord, yCoord));
  654. Console.WriteLine("parsed ellipse point: " + xCoord + ";" + yCoord + " pointCount: " + (samplingRateEllipse / 4));
  655. }
  656. for (int j = 0; j < samplingRateEllipse / 4; j++) //create actual points for fourth quadrant
  657. {
  658. int xCoord = Convert.ToInt32(Math.Round(x - yValues[j]));
  659. int yCoord = Convert.ToInt32(Math.Round(y - xValues[j] * yScale));
  660. ellipse.Add(ScaleAndCreatePoint(xCoord, yCoord));
  661. Console.WriteLine("parsed ellipse point: " + xCoord + ";" + yCoord + " pointCount: " + (samplingRateEllipse / 4));
  662. }
  663. ellipse.Add(ScaleAndCreatePoint(Convert.ToInt32(Math.Round(x + 0)), Convert.ToInt32(Math.Round(y - rx * yScale)))); //close ellipse
  664. return ellipse;
  665. }
  666. }
  667. }