PolyLineObject.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950
  1. using FastReport.Utils;
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.ComponentModel;
  6. using System.Drawing;
  7. using System.Drawing.Drawing2D;
  8. using System.Globalization;
  9. using System.Text;
  10. namespace FastReport
  11. {
  12. /// <summary>
  13. /// Represents a poly line object.
  14. /// </summary>
  15. /// <remarks>
  16. /// Use the <b>Border.Width</b>, <b>Border.Style</b> and <b>Border.Color</b> properties to set
  17. /// the line width, style and color.
  18. /// </remarks>
  19. public partial class PolyLineObject : ReportComponentBase
  20. {
  21. #region Protected Internal Fields
  22. /// <summary>
  23. /// do not set this value, internal use only
  24. /// </summary>
  25. protected internal PolygonSelectionMode polygonSelectionMode;
  26. #endregion Protected Internal Fields
  27. #region Private Fields
  28. private PointF center;
  29. private PolyPointCollection pointsCollection;
  30. #endregion Private Fields
  31. #region Public Properties
  32. /// <summary>
  33. /// Return points collection.
  34. /// You can modify the collection for change this object.
  35. /// </summary>
  36. public PolyPointCollection Points
  37. {
  38. get
  39. {
  40. return pointsCollection;
  41. }
  42. }
  43. /// <summary>
  44. /// Returns origin of coordinates relative to the top left corner
  45. /// </summary>
  46. [Browsable(false)]
  47. public float CenterX { get { return center.X; } set { center.X = value; } }
  48. /// <summary>
  49. /// Returns origin of coordinates relative to the top left corner
  50. /// </summary>
  51. [Browsable(false)]
  52. public float CenterY { get { return center.Y; } set { center.Y = value; } }
  53. /// <summary>
  54. /// Return points array of line
  55. /// deprecated
  56. /// </summary>
  57. [Browsable(false)]
  58. [Obsolete]
  59. public PointF[] PointsArray
  60. {
  61. get
  62. {
  63. List<PointF> result = new List<PointF>();
  64. foreach (PolyPoint point in pointsCollection)
  65. {
  66. result.Add(new PointF(point.X, point.Y));
  67. }
  68. return result.ToArray();
  69. }
  70. }
  71. /// <summary>
  72. /// Return point types array. 0 - Start of line, 1 - Keep on line
  73. /// deprecated
  74. /// </summary>
  75. [Browsable(false)]
  76. [Obsolete]
  77. public byte[] PointTypesArray
  78. {
  79. get
  80. {
  81. List<byte> result = new List<byte>();
  82. result.Add(0);
  83. for (int i = 1; i < pointsCollection.Count; i++)
  84. result.Add(1);
  85. return result.ToArray();
  86. }
  87. }
  88. #endregion Public Properties
  89. #region Public Constructors
  90. /// <summary>
  91. /// Initializes a new instance of the <see cref="LineObject"/> class with default settings.
  92. /// </summary>
  93. public PolyLineObject()
  94. {
  95. FlagSimpleBorder = true;
  96. FlagUseFill = false;
  97. pointsCollection = new PolyPointCollection();
  98. center = PointF.Empty;
  99. InitDesign();
  100. }
  101. #endregion Public Constructors
  102. #region Public Methods
  103. /// <inheritdoc/>
  104. public override void Assign(Base source)
  105. {
  106. base.Assign(source);
  107. PolyLineObject src = source as PolyLineObject;
  108. pointsCollection = src.pointsCollection.Clone();
  109. center = src.center;
  110. //recalculateBounds();
  111. }
  112. /// <inheritdoc/>
  113. public override void Deserialize(FRReader reader)
  114. {
  115. base.Deserialize(reader);
  116. pointsCollection.Clear();
  117. if (reader.HasProperty("PolyPoints"))
  118. {
  119. string polyPoints = reader.ReadStr("PolyPoints");
  120. foreach (string str in polyPoints.Split('|'))
  121. {
  122. string[] point = str.Split('\\');
  123. if (point.Length == 3)
  124. {
  125. float f1 = float.Parse(point[0].Replace(',', '.'), CultureInfo.InvariantCulture);
  126. float f2 = float.Parse(point[1].Replace(',', '.'), CultureInfo.InvariantCulture);
  127. pointsCollection.Add(new PolyPoint(f1, f2));
  128. }
  129. }
  130. }
  131. else if (reader.HasProperty("PolyPoints_v2"))
  132. {
  133. string polyPoints = reader.ReadStr("PolyPoints_v2");
  134. foreach (string str in polyPoints.Split('|'))
  135. {
  136. PolyPoint point = new PolyPoint();
  137. point.Deserialize(str);
  138. pointsCollection.Add(point);
  139. }
  140. }
  141. if (reader.HasProperty("CenterX"))
  142. center.X = reader.ReadFloat("CenterX");
  143. if (reader.HasProperty("CenterY"))
  144. center.Y = reader.ReadFloat("CenterY");
  145. //recalculateBounds();
  146. }
  147. /// <inheritdoc/>
  148. public override void Draw(FRPaintEventArgs e)
  149. {
  150. switch (pointsCollection.Count)
  151. {
  152. case 0:
  153. case 1:
  154. IGraphics g = e.Graphics;
  155. float x = AbsLeft + CenterX;
  156. float y = AbsTop + CenterY;
  157. if(pointsCollection.Count == 1)
  158. {
  159. x += pointsCollection[0].X;
  160. y += pointsCollection[0].Y;
  161. }
  162. g.DrawLine(Pens.Black, x * e.ScaleX - 6, y * e.ScaleY, x * e.ScaleX + 6, y * e.ScaleY);
  163. g.DrawLine(Pens.Black, x * e.ScaleX, y * e.ScaleY - 6, x * e.ScaleX, y * e.ScaleY + 6);
  164. break;
  165. default:
  166. DoDrawPoly(e);
  167. DrawDesign0(e);
  168. break;
  169. }
  170. DrawDesign1(e);
  171. }
  172. /// <summary>
  173. /// Calculate GraphicsPath for draw to page
  174. /// </summary>
  175. /// <param name="pen">Pen for lines</param>
  176. /// <param name="left">Left boundary</param>
  177. /// <param name="top">Top boundary</param>
  178. /// <param name="right">Right boundary</param>
  179. /// <param name="bottom">Bottom boundary</param>
  180. /// <param name="scaleX">scale by width</param>
  181. /// <param name="scaleY">scale by height</param>
  182. /// <returns>Always returns a non-empty path</returns>
  183. public GraphicsPath GetPath(Pen pen, float left, float top, float right, float bottom, float scaleX, float scaleY)
  184. {
  185. if (pointsCollection.Count == 0)
  186. {
  187. GraphicsPath result = new GraphicsPath();
  188. result.AddLine(left * scaleX, top * scaleX, (right + 1) * scaleX, (bottom + 1) * scaleX);
  189. return result;
  190. }
  191. else if (pointsCollection.Count == 1)
  192. {
  193. GraphicsPath result = new GraphicsPath();
  194. left = left + CenterX + pointsCollection[0].X;
  195. top = top + CenterY + pointsCollection[0].Y;
  196. result.AddLine(left * scaleX, top * scaleX, (left + 1) * scaleX, (top + 1) * scaleX);
  197. return result;
  198. }
  199. List<PointF> aPoints = new List<PointF>();
  200. List<byte> pointTypes = new List<byte>();
  201. PolyPoint prev = null;
  202. PolyPoint point = pointsCollection[0];
  203. aPoints.Add(new PointF((point.X + left + center.X) * scaleX, (point.Y + top + center.Y) * scaleY));
  204. pointTypes.Add(0);
  205. int count = pointsCollection.Count;
  206. if (this is PolygonObject)
  207. {
  208. count++;
  209. }
  210. for (int i = 1; i < count; i++)
  211. {
  212. prev = point;
  213. point = pointsCollection[i];
  214. //is bezier?
  215. if (prev.RightCurve != null || point.LeftCurve != null)
  216. {
  217. if (prev.RightCurve != null)
  218. {
  219. aPoints.Add(new PointF((prev.X + left + center.X + prev.RightCurve.X) * scaleX, (prev.Y + top + center.Y + prev.RightCurve.Y) * scaleY));
  220. pointTypes.Add(3);
  221. }
  222. else
  223. {
  224. PolyPoint pseudo = GetPseudoPoint(prev, point);
  225. aPoints.Add(new PointF((pseudo.X + left + center.X) * scaleX, (pseudo.Y + top + center.Y) * scaleY));
  226. pointTypes.Add(3);
  227. }
  228. if (point.LeftCurve != null)
  229. {
  230. aPoints.Add(new PointF((point.X + left + center.X + point.LeftCurve.X) * scaleX, (point.Y + top + center.Y + point.LeftCurve.Y) * scaleY));
  231. pointTypes.Add(3);
  232. }
  233. else
  234. {
  235. PolyPoint pseudo = GetPseudoPoint(point, prev);
  236. aPoints.Add(new PointF((pseudo.X + left + center.X) * scaleX, (pseudo.Y + top + center.Y) * scaleY));
  237. pointTypes.Add(3);
  238. }
  239. aPoints.Add(new PointF((point.X + left + center.X) * scaleX, (point.Y + top + center.Y) * scaleY));
  240. pointTypes.Add(3);
  241. }
  242. else
  243. {
  244. aPoints.Add(new PointF((point.X + left + center.X) * scaleX, (point.Y + top + center.Y) * scaleY));
  245. pointTypes.Add(1);
  246. }
  247. }
  248. return new GraphicsPath(aPoints.ToArray(), pointTypes.ToArray());
  249. }
  250. /// <summary>
  251. /// Recalculate position and size of element
  252. /// </summary>
  253. public void RecalculateBounds()
  254. {
  255. if (pointsCollection.Count > 0)
  256. {
  257. // init
  258. PolyPoint prev = null;
  259. PolyPoint point = pointsCollection[0];
  260. float left = point.X;
  261. float top = point.Y;
  262. float right = point.X;
  263. float bottom = point.Y;
  264. int count = pointsCollection.Count;
  265. if (this is PolygonObject)
  266. count++;
  267. // stage 1 calculate min bounds
  268. foreach (PolyPoint pnt in pointsCollection)
  269. {
  270. if (pnt.X < left)
  271. left = pnt.X;
  272. else if (pnt.X > right)
  273. right = pnt.X;
  274. if (pnt.Y < top)
  275. top = pnt.Y;
  276. else if (pnt.Y > bottom)
  277. bottom = pnt.Y;
  278. }
  279. // stage 2 check if one of bezier way point is outside
  280. for (int i = 1; i < count; i++)
  281. {
  282. prev = point;
  283. point = pointsCollection[i];
  284. bool haveToCalculate = false;
  285. PolyPoint p_1 = null;
  286. PolyPoint p_2 = null;
  287. if (prev.RightCurve != null)
  288. {
  289. p_1 = new PolyPoint(prev.X + prev.RightCurve.X, prev.Y + prev.RightCurve.Y);
  290. if (p_1.X < left)
  291. haveToCalculate = true;
  292. else if (p_1.X > right)
  293. haveToCalculate = true;
  294. if (p_1.Y < top)
  295. haveToCalculate = true;
  296. else if (p_1.Y > bottom)
  297. haveToCalculate = true;
  298. }
  299. if (point.LeftCurve != null)
  300. {
  301. p_2 = new PolyPoint(point.X + point.LeftCurve.X, point.Y + point.LeftCurve.Y);
  302. if (p_2.X < left)
  303. haveToCalculate = true;
  304. else if (p_2.X > right)
  305. haveToCalculate = true;
  306. if (p_2.Y < top)
  307. haveToCalculate = true;
  308. else if (p_2.Y > bottom)
  309. haveToCalculate = true;
  310. }
  311. if (haveToCalculate)
  312. {
  313. if (p_1 == null)
  314. p_1 = GetPseudoPoint(prev, point);
  315. if (p_2 == null)
  316. p_2 = GetPseudoPoint(point, prev);
  317. // now calculate extrema
  318. // x
  319. float delta = RecalculateBounds_Delta(prev.X, p_1.X, p_2.X, point.X);
  320. if (delta > 0)
  321. {
  322. delta = (float)Math.Sqrt(delta);
  323. float t_1 = RecalculateBounds_Solve(prev.X, p_1.X, p_2.X, point.X, -delta);
  324. if (0 < t_1 && t_1 < 1)
  325. {
  326. float x = RecalculateBounds_Value(prev.X, p_1.X, p_2.X, point.X, t_1);
  327. if (x < left)
  328. left = x;
  329. else if (x > right)
  330. right = x;
  331. }
  332. float t_2 = RecalculateBounds_Solve(prev.X, p_1.X, p_2.X, point.X, delta);
  333. if (0 < t_2 && t_2 < 1)
  334. {
  335. float x = RecalculateBounds_Value(prev.X, p_1.X, p_2.X, point.X, t_2);
  336. if (x < left)
  337. left = x;
  338. else if (x > right)
  339. right = x;
  340. }
  341. }
  342. // y
  343. delta = RecalculateBounds_Delta(prev.Y, p_1.Y, p_2.Y, point.Y);
  344. if (delta > 0)
  345. {
  346. delta = (float)Math.Sqrt(delta);
  347. float t_1 = RecalculateBounds_Solve(prev.Y, p_1.Y, p_2.Y, point.Y, -delta);
  348. if (0 < t_1 && t_1 < 1)
  349. {
  350. float y = RecalculateBounds_Value(prev.Y, p_1.Y, p_2.Y, point.Y, t_1);
  351. if (y < top)
  352. top = y;
  353. else if (y > bottom)
  354. bottom = y;
  355. }
  356. float t_2 = RecalculateBounds_Solve(prev.Y, p_1.Y, p_2.Y, point.Y, delta);
  357. if (0 < t_2 && t_2 < 1)
  358. {
  359. float y = RecalculateBounds_Value(prev.Y, p_1.Y, p_2.Y, point.Y, t_2);
  360. if (y < top)
  361. top = y;
  362. else if (y > bottom)
  363. bottom = y;
  364. }
  365. }
  366. }
  367. }
  368. // update
  369. float centerX = center.X;
  370. float centerY = center.Y;
  371. center.X = -left;
  372. center.Y = -top;
  373. base.Left += left + centerX;
  374. base.Top += top + centerY;
  375. base.Height = bottom - top;
  376. base.Width = right - left;
  377. }
  378. else
  379. {
  380. CenterX = 0;
  381. CenterY = 0;
  382. base.Width = 5;
  383. base.Height = 5;
  384. }
  385. }
  386. private float RecalculateBounds_Delta(float p_0,float p_1, float p_2, float p_3)
  387. {
  388. return p_1 * p_1 - p_0 * p_2 - p_1 * p_2 + p_2 * p_2 + p_0 * p_3 - p_1 * p_3;
  389. }
  390. private float RecalculateBounds_Solve(float p_0, float p_1, float p_2, float p_3, float deltaSqrt)
  391. {
  392. return (p_0 - 2 * p_1 + p_2 + deltaSqrt) / (p_0 - 3 * p_1 + 3 * p_2 - p_3);
  393. }
  394. private float RecalculateBounds_Value(float p_0, float p_1, float p_2, float p_3, float t)
  395. {
  396. float t1 = 1 - t;
  397. return p_0 * t1 * t1 * t1 + 3 * p_1 * t1 * t1 * t + 3 * p_2 * t1 * t * t + p_3 * t * t * t;
  398. }
  399. /// <inheritdoc/>
  400. public override void Serialize(FRWriter writer)
  401. {
  402. Border.SimpleBorder = true;
  403. base.Serialize(writer);
  404. PolyLineObject c = writer.DiffObject as PolyLineObject;
  405. StringBuilder sb = new StringBuilder(pointsCollection.Count * 10);
  406. foreach (PolyPoint point in pointsCollection)
  407. {
  408. point.Serialize(sb);
  409. sb.Append("|");
  410. }
  411. if (sb.Length > 0)
  412. sb.Length--;
  413. writer.WriteStr("PolyPoints_v2", sb.ToString());
  414. writer.WriteFloat("CenterX", center.X);
  415. writer.WriteFloat("CenterY", center.Y);
  416. }
  417. public void SetPolyLine(PointF[] newPoints)
  418. {
  419. pointsCollection.Clear();
  420. if (newPoints != null)
  421. {
  422. CenterX = 0;
  423. CenterY = 0;
  424. foreach (PointF point in newPoints)
  425. {
  426. pointsCollection.Add(new PolyPoint(point.X, point.Y));
  427. }
  428. }
  429. float l = Left;
  430. float t = Top;
  431. RecalculateBounds();
  432. Left = l;
  433. Top = t;
  434. }
  435. #endregion Public Methods
  436. #region Internal Methods
  437. internal void DoDrawPoly(FRPaintEventArgs e)
  438. {
  439. IGraphics g = e.Graphics;
  440. Report report = Report;
  441. if (report != null && report.SmoothGraphics)
  442. {
  443. g.InterpolationMode = InterpolationMode.HighQualityBicubic;
  444. g.SmoothingMode = SmoothingMode.AntiAlias;
  445. }
  446. drawPoly(e);
  447. if (report != null && report.SmoothGraphics)
  448. {
  449. g.InterpolationMode = InterpolationMode.Default;
  450. g.SmoothingMode = SmoothingMode.Default;
  451. }
  452. }
  453. #endregion Internal Methods
  454. #region Protected Methods
  455. /// <summary>
  456. /// Add point to end of polyline, need to recalculate bounds after add
  457. /// First point must have zero coordinate and zero type.
  458. /// Recalculate bounds.
  459. /// Method is slow do not use this.
  460. /// </summary>
  461. /// <param name="localX">local x - relative to left-top point</param>
  462. /// <param name="localY">local y - relative to left-top point</param>
  463. /// <param name="pointType">depreceted</param>
  464. protected PolyPoint addPoint(float localX, float localY, byte pointType)
  465. {
  466. PolyPoint result;
  467. pointsCollection.Add(result = new PolyPoint(localX, localY));
  468. RecalculateBounds();
  469. return result;
  470. }
  471. /// <summary>
  472. /// Delete point from polyline by index.
  473. /// Recalculate bounds.
  474. /// Method is slow do not use this.
  475. /// </summary>
  476. /// <param name="index">Index of point in polyline</param>
  477. protected void deletePoint(int index)
  478. {
  479. pointsCollection.Remove(index);
  480. RecalculateBounds();
  481. }
  482. /// <summary>
  483. /// Draw polyline path to graphics
  484. /// </summary>
  485. /// <param name="e">Event arguments</param>
  486. protected virtual void drawPoly(FRPaintEventArgs e)
  487. {
  488. Pen pen;
  489. if (polygonSelectionMode == PolygonSelectionMode.MoveAndScale)
  490. pen = e.Cache.GetPen(Border.Color, Border.Width * e.ScaleX, Border.DashStyle);
  491. else pen = e.Cache.GetPen(Border.Color, 1, DashStyle.Solid);
  492. using (GraphicsPath path = GetPath(pen, AbsLeft, AbsTop, AbsRight, AbsBottom, e.ScaleX, e.ScaleY))
  493. e.Graphics.DrawPath(pen, path);
  494. }
  495. /// <summary>
  496. /// Insert point to desired place of polyline
  497. /// recalculateBounds();
  498. /// Method is slow do not use this
  499. /// </summary>
  500. /// <param name="index">Index of place from zero to count</param>
  501. /// <param name="localX">local x - relative to left-top point</param>
  502. /// <param name="localY">local y - relative to left-top point</param>
  503. /// <param name="pointType">deprecated</param>
  504. protected PolyPoint insertPoint(int index, float localX, float localY, byte pointType)
  505. {
  506. PolyPoint result;
  507. pointsCollection.Insert(index, result = new PolyPoint(localX, localY));
  508. RecalculateBounds();
  509. return result;
  510. }
  511. #endregion Protected Methods
  512. #region Private Methods
  513. private float getDistance(float px, float py, float px0, float py0, float px1, float py1, out int index)
  514. {
  515. float vx = px1 - px0;
  516. float vy = py1 - py0;
  517. float wx = px - px0;
  518. float wy = py - py0;
  519. float c1 = vx * wx + vy * wy;
  520. if (c1 <= 0) { index = -1; return (px0 - px) * (px0 - px) + (py0 - py) * (py0 - py); }
  521. float c2 = vx * vx + vy * vy;
  522. if (c2 <= c1) { index = 1; return (px1 - px) * (px1 - px) + (py1 - py) * (py1 - py); }
  523. float b = c1 / c2;
  524. index = 0;
  525. float bx = px0 + vx * b;
  526. float by = py0 + vy * b;
  527. return (bx - px) * (bx - px) + (by - py) * (by - py);
  528. }
  529. private PolyPoint GetPseudoPoint(PolyPoint start, PolyPoint end)
  530. {
  531. float vecX = end.X - start.X;
  532. float vecY = end.Y - start.Y;
  533. float distance = (float)Math.Sqrt(vecX * vecX + vecY * vecY);
  534. vecX = vecX / 3;
  535. vecY = vecY / 3;
  536. return new PolyPoint(start.X + vecX, start.Y + vecY);
  537. }
  538. #endregion Private Methods
  539. #region Protected Internal Enums
  540. protected internal enum PolygonSelectionMode : int
  541. {
  542. MoveAndScale,
  543. Normal,
  544. AddToLine,
  545. AddBezier,
  546. Delete
  547. }
  548. #endregion Protected Internal Enums
  549. #region Public Classes
  550. /// <summary>
  551. /// Represent a point for polygon object
  552. /// </summary>
  553. public class PolyPoint
  554. {
  555. #region Private Fields
  556. private static readonly NumberFormatInfo invariant;
  557. private PolyPoint left;
  558. private PolyPoint right;
  559. private float x;
  560. private float y;
  561. #endregion Private Fields
  562. #region Public Properties
  563. public PolyPoint LeftCurve
  564. {
  565. get { return left; }
  566. set { left = value; }
  567. }
  568. public PolyPoint RightCurve
  569. {
  570. get { return right; }
  571. set { right = value; }
  572. }
  573. public float X
  574. {
  575. get { return x; }
  576. set { x = value; }
  577. }
  578. public float Y
  579. {
  580. get { return y; }
  581. set { y = value; }
  582. }
  583. #endregion Public Properties
  584. #region Public Constructors
  585. static PolyPoint()
  586. {
  587. invariant = new NumberFormatInfo();
  588. invariant.NumberGroupSeparator = String.Empty;
  589. invariant.NumberDecimalSeparator = ".";
  590. }
  591. public PolyPoint()
  592. {
  593. }
  594. public PolyPoint(float x, float y)
  595. {
  596. this.x = x;
  597. this.y = y;
  598. }
  599. #endregion Public Constructors
  600. #region Public Methods
  601. public void Deserialize(string s)
  602. {
  603. string[] strs = s.Split('/');
  604. int index = 0;
  605. Deserialize(strs, ref index);
  606. if (index < strs.Length)
  607. {
  608. if (strs[index] == "L")
  609. {
  610. index++;
  611. LeftCurve = new PolyPoint();
  612. LeftCurve.Deserialize(strs, ref index);
  613. }
  614. if (index < strs.Length)
  615. {
  616. if (strs[index] == "R")
  617. {
  618. index++;
  619. RightCurve = new PolyPoint();
  620. RightCurve.Deserialize(strs, ref index);
  621. }
  622. }
  623. }
  624. }
  625. public bool Near(PolyPoint p)
  626. {
  627. return (p != null) && (Math.Abs(x - p.x) < 0.0001) && (Math.Abs(y - p.y) < 0.0001);
  628. }
  629. public void ScaleX(float scale)
  630. {
  631. x *= scale;
  632. if (LeftCurve != null)
  633. LeftCurve.X *= scale;
  634. if (RightCurve != null)
  635. RightCurve.X *= scale;
  636. }
  637. public void ScaleY(float scale)
  638. {
  639. y *= scale;
  640. if (LeftCurve != null)
  641. LeftCurve.Y *= scale;
  642. if (RightCurve != null)
  643. RightCurve.Y *= scale;
  644. }
  645. public void Serialize(StringBuilder sb)
  646. {
  647. sb.Append(Round(x)).Append("/").Append(Round(y));
  648. if (LeftCurve != null)
  649. {
  650. sb.Append("/L/").Append(Round(LeftCurve.X)).Append("/").Append(Round(LeftCurve.Y));
  651. }
  652. if (RightCurve != null)
  653. {
  654. sb.Append("/R/").Append(Round(RightCurve.X)).Append("/").Append(Round(RightCurve.Y));
  655. }
  656. }
  657. public override string ToString()
  658. {
  659. return "(" + Round(x) + ";" + Round(y) + ")";
  660. }
  661. #endregion Public Methods
  662. #region Private Methods
  663. private void Deserialize(string[] strs, ref int index)
  664. {
  665. for (int i = 0; i < 2 && index < strs.Length; i++)
  666. {
  667. switch (i)
  668. {
  669. case 0:
  670. Single.TryParse(strs[index], NumberStyles.Float, invariant, out x);
  671. break;
  672. case 1:
  673. Single.TryParse(strs[index], NumberStyles.Float, invariant, out y);
  674. break;
  675. }
  676. index++;
  677. }
  678. }
  679. private string Round(float value)
  680. {
  681. return Convert.ToString(Math.Round(value, 4), invariant);
  682. }
  683. internal PolyPoint Clone()
  684. {
  685. PolyPoint result = new PolyPoint(x, y);
  686. if (LeftCurve != null)
  687. result.LeftCurve = LeftCurve.Clone();
  688. if (RightCurve != null)
  689. result.RightCurve = RightCurve.Clone();
  690. return result;
  691. }
  692. #endregion Private Methods
  693. }
  694. public class PolyPointCollection : IEnumerable<PolyPoint>
  695. {
  696. #region Private Fields
  697. private List<PolyPoint> points;
  698. #endregion Private Fields
  699. #region Public Indexers
  700. public PolyPoint this[int index]
  701. {
  702. get
  703. {
  704. index = NormalizeIndex(index);
  705. return points[index];
  706. }
  707. set
  708. {
  709. index = NormalizeIndex(index);
  710. points[index] = value;
  711. }
  712. }
  713. #endregion Public Indexers
  714. #region Public Properties
  715. public int Count
  716. {
  717. get
  718. {
  719. return points.Count;
  720. }
  721. }
  722. public bool IsReadOnly
  723. {
  724. get
  725. {
  726. return false;
  727. }
  728. }
  729. #endregion Public Properties
  730. #region Public Constructors
  731. public PolyPointCollection()
  732. {
  733. points = new List<PolyPoint>();
  734. }
  735. #endregion Public Constructors
  736. #region Public Methods
  737. public void Add(PolyPoint item)
  738. {
  739. points.Add(item);
  740. }
  741. public void Clear()
  742. {
  743. points.Clear();
  744. }
  745. public PolyPointCollection Clone()
  746. {
  747. PolyPointCollection result = new PolyPointCollection();
  748. result.points = new List<PolyPoint>();
  749. foreach(PolyPoint point in points)
  750. {
  751. result.points.Add(point.Clone());
  752. }
  753. return result;
  754. }
  755. public IEnumerator<PolyPoint> GetEnumerator()
  756. {
  757. return points.GetEnumerator();
  758. }
  759. IEnumerator IEnumerable.GetEnumerator()
  760. {
  761. return points.GetEnumerator();
  762. }
  763. public int IndexOf(PolyPoint currentPoint)
  764. {
  765. return points.IndexOf(currentPoint);
  766. }
  767. public void Insert(int index, PolyPoint item)
  768. {
  769. int count = points.Count;
  770. if (count > 0)
  771. {
  772. while (index < 0)
  773. index += count;
  774. while (index > count)
  775. index -= count;
  776. }
  777. points.Insert(index, item);
  778. }
  779. public void Remove(int index)
  780. {
  781. index = NormalizeIndex(index);
  782. points.RemoveAt(index);
  783. }
  784. #endregion Public Methods
  785. #region Private Methods
  786. private int NormalizeIndex(int index)
  787. {
  788. int count = points.Count;
  789. if (count == 0)
  790. return 0;
  791. if (index >= 0 && index < count)
  792. return index;
  793. while (index < 0)
  794. index += count;
  795. while (index >= count)
  796. index -= count;
  797. return index;
  798. }
  799. #endregion Private Methods
  800. }
  801. #endregion Public Classes
  802. }
  803. }