PolyLineObject.cs 31 KB

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