SvgLinearGradientServer.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Drawing;
  5. using System.Drawing.Drawing2D;
  6. using System.Linq;
  7. #pragma warning disable
  8. namespace Svg
  9. {
  10. [SvgElement("linearGradient")]
  11. public sealed class SvgLinearGradientServer : SvgGradientServer
  12. {
  13. [SvgAttribute("x1")]
  14. public SvgUnit X1
  15. {
  16. get
  17. {
  18. return this.Attributes.GetAttribute<SvgUnit>("x1");
  19. }
  20. set
  21. {
  22. Attributes["x1"] = value;
  23. }
  24. }
  25. [SvgAttribute("y1")]
  26. public SvgUnit Y1
  27. {
  28. get
  29. {
  30. return this.Attributes.GetAttribute<SvgUnit>("y1");
  31. }
  32. set
  33. {
  34. this.Attributes["y1"] = value;
  35. }
  36. }
  37. [SvgAttribute("x2")]
  38. public SvgUnit X2
  39. {
  40. get
  41. {
  42. return this.Attributes.GetAttribute<SvgUnit>("x2");
  43. }
  44. set
  45. {
  46. Attributes["x2"] = value;
  47. }
  48. }
  49. [SvgAttribute("y2")]
  50. public SvgUnit Y2
  51. {
  52. get
  53. {
  54. return this.Attributes.GetAttribute<SvgUnit>("y2");
  55. }
  56. set
  57. {
  58. this.Attributes["y2"] = value;
  59. }
  60. }
  61. private bool IsInvalid
  62. {
  63. get
  64. {
  65. // Need at least 2 colours to do the gradient fill
  66. return this.Stops.Count < 2;
  67. }
  68. }
  69. public SvgLinearGradientServer()
  70. {
  71. X1 = new SvgUnit(SvgUnitType.Percentage, 0F);
  72. Y1 = new SvgUnit(SvgUnitType.Percentage, 0F);
  73. X2 = new SvgUnit(SvgUnitType.Percentage, 100F);
  74. Y2 = new SvgUnit(SvgUnitType.Percentage, 0F);
  75. }
  76. public override Brush GetBrush(SvgVisualElement renderingElement, ISvgRenderer renderer, float opacity, bool forStroke = false)
  77. {
  78. LoadStops(renderingElement);
  79. if (this.Stops.Count < 1) return null;
  80. if (this.Stops.Count == 1)
  81. {
  82. var stopColor = this.Stops[0].GetColor(renderingElement);
  83. int alpha = (int)Math.Round((opacity * (stopColor.A / 255.0f)) * 255);
  84. Color colour = System.Drawing.Color.FromArgb(alpha, stopColor);
  85. return new SolidBrush(colour);
  86. }
  87. try
  88. {
  89. if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.SetBoundable(renderingElement);
  90. var points = new PointF[] {
  91. SvgUnit.GetDevicePoint(NormalizeUnit(this.X1), NormalizeUnit(this.Y1), renderer, this),
  92. SvgUnit.GetDevicePoint(NormalizeUnit(this.X2), NormalizeUnit(this.Y2), renderer, this)
  93. };
  94. var bounds = renderer.GetBoundable().Bounds;
  95. if (bounds.Width <= 0 || bounds.Height <= 0 || ((points[0].X == points[1].X) && (points[0].Y == points[1].Y)))
  96. {
  97. if (this.GetCallback != null) return GetCallback().GetBrush(renderingElement, renderer, opacity, forStroke);
  98. return null;
  99. }
  100. using (var transform = EffectiveGradientTransform)
  101. {
  102. var midPoint = new PointF((points[0].X + points[1].X) / 2, (points[0].Y + points[1].Y) / 2);
  103. transform.Translate(bounds.X, bounds.Y, MatrixOrder.Prepend);
  104. if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox)
  105. {
  106. // Transform a normal (i.e. perpendicular line) according to the transform
  107. transform.Scale(bounds.Width, bounds.Height, MatrixOrder.Prepend);
  108. transform.RotateAt(-90.0f, midPoint, MatrixOrder.Prepend);
  109. }
  110. transform.TransformPoints(points);
  111. }
  112. if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox)
  113. {
  114. // Transform the normal line back to a line such that the gradient still starts in the correct corners, but
  115. // has the proper normal vector based on the transforms. If you work out the geometry, these formulas should work.
  116. var midPoint = new PointF((points[0].X + points[1].X) / 2, (points[0].Y + points[1].Y) / 2);
  117. var dy = (points[1].Y - points[0].Y);
  118. var dx = (points[1].X - points[0].X);
  119. var x2 = points[0].X;
  120. var y2 = points[1].Y;
  121. if (Math.Round(dx, 4) == 0)
  122. {
  123. points[0] = new PointF(midPoint.X + dy / 2 * bounds.Width / bounds.Height, midPoint.Y);
  124. points[1] = new PointF(midPoint.X - dy / 2 * bounds.Width / bounds.Height, midPoint.Y);
  125. }
  126. else if (Math.Round(dy, 4) == 0)
  127. {
  128. points[0] = new PointF(midPoint.X, midPoint.Y - dx / 2 * bounds.Height / bounds.Width);
  129. points[1] = new PointF(midPoint.X, midPoint.Y + dx / 2 * bounds.Height / bounds.Width); ;
  130. }
  131. else
  132. {
  133. var startX = (float)((dy * dx * (midPoint.Y - y2) + Math.Pow(dx, 2) * midPoint.X + Math.Pow(dy, 2) * x2) /
  134. (Math.Pow(dx, 2) + Math.Pow(dy, 2)));
  135. var startY = dy * (startX - x2) / dx + y2;
  136. points[0] = new PointF(startX, startY);
  137. points[1] = new PointF(midPoint.X + (midPoint.X - startX), midPoint.Y + (midPoint.Y - startY));
  138. }
  139. }
  140. var effectiveStart = points[0];
  141. var effectiveEnd = points[1];
  142. if (PointsToMove(renderingElement, points[0], points[1]) > LinePoints.None)
  143. {
  144. var expansion = ExpandGradient(renderingElement, points[0], points[1]);
  145. effectiveStart = expansion.StartPoint;
  146. effectiveEnd = expansion.EndPoint;
  147. }
  148. var result = new LinearGradientBrush(effectiveStart, effectiveEnd, System.Drawing.Color.Transparent, System.Drawing.Color.Transparent)
  149. {
  150. InterpolationColors = CalculateColorBlend(renderer, opacity, points[0], effectiveStart, points[1], effectiveEnd),
  151. WrapMode = WrapMode.TileFlipX
  152. };
  153. return result;
  154. }
  155. finally
  156. {
  157. if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.PopBoundable();
  158. }
  159. }
  160. private SvgUnit NormalizeUnit(SvgUnit orig)
  161. {
  162. return (orig.Type == SvgUnitType.Percentage && this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox ?
  163. new SvgUnit(SvgUnitType.User, orig.Value / 100) :
  164. orig);
  165. }
  166. [Flags]
  167. private enum LinePoints
  168. {
  169. None = 0,
  170. Start = 1,
  171. End = 2
  172. }
  173. private LinePoints PointsToMove(ISvgBoundable boundable, PointF specifiedStart, PointF specifiedEnd)
  174. {
  175. var bounds = boundable.Bounds;
  176. if (specifiedStart.X == specifiedEnd.X)
  177. {
  178. return (bounds.Top < specifiedStart.Y && specifiedStart.Y < bounds.Bottom ? LinePoints.Start : LinePoints.None) |
  179. (bounds.Top < specifiedEnd.Y && specifiedEnd.Y < bounds.Bottom ? LinePoints.End : LinePoints.None);
  180. }
  181. else if (specifiedStart.Y == specifiedEnd.Y)
  182. {
  183. return (bounds.Left < specifiedStart.X && specifiedStart.X < bounds.Right ? LinePoints.Start : LinePoints.None) |
  184. (bounds.Left < specifiedEnd.X && specifiedEnd.X < bounds.Right ? LinePoints.End : LinePoints.None);
  185. }
  186. return (boundable.Bounds.Contains(specifiedStart) ? LinePoints.Start : LinePoints.None) |
  187. (boundable.Bounds.Contains(specifiedEnd) ? LinePoints.End : LinePoints.None);
  188. }
  189. #if READONLY_STRUCTS
  190. public readonly struct GradientPoints
  191. #else
  192. public struct GradientPoints
  193. #endif
  194. {
  195. public readonly PointF StartPoint;
  196. public readonly PointF EndPoint;
  197. public GradientPoints(PointF startPoint, PointF endPoint)
  198. {
  199. this.StartPoint = startPoint;
  200. this.EndPoint = endPoint;
  201. }
  202. }
  203. private GradientPoints ExpandGradient(ISvgBoundable boundable, PointF specifiedStart, PointF specifiedEnd)
  204. {
  205. var pointsToMove = PointsToMove(boundable, specifiedStart, specifiedEnd);
  206. if (pointsToMove == LinePoints.None)
  207. {
  208. Debug.Fail("Unexpectedly expanding gradient when not needed!");
  209. return new GradientPoints(specifiedStart, specifiedEnd);
  210. }
  211. var bounds = boundable.Bounds;
  212. var effectiveStart = specifiedStart;
  213. var effectiveEnd = specifiedEnd;
  214. var intersectionPoints = CandidateIntersections(bounds, specifiedStart, specifiedEnd);
  215. Debug.Assert(intersectionPoints.Count == 2, "Unanticipated number of intersection points");
  216. if (!(Math.Sign(intersectionPoints[1].X - intersectionPoints[0].X) == Math.Sign(specifiedEnd.X - specifiedStart.X) &&
  217. Math.Sign(intersectionPoints[1].Y - intersectionPoints[0].Y) == Math.Sign(specifiedEnd.Y - specifiedStart.Y)))
  218. {
  219. intersectionPoints = intersectionPoints.Reverse().ToList();
  220. }
  221. if ((pointsToMove & LinePoints.Start) > 0) effectiveStart = intersectionPoints[0];
  222. if ((pointsToMove & LinePoints.End) > 0) effectiveEnd = intersectionPoints[1];
  223. switch (SpreadMethod)
  224. {
  225. case SvgGradientSpreadMethod.Reflect:
  226. case SvgGradientSpreadMethod.Repeat:
  227. var specifiedLength = CalculateDistance(specifiedStart, specifiedEnd);
  228. var specifiedUnitVector = new PointF((specifiedEnd.X - specifiedStart.X) / (float)specifiedLength, (specifiedEnd.Y - specifiedStart.Y) / (float)specifiedLength);
  229. var oppUnitVector = new PointF(-specifiedUnitVector.X, -specifiedUnitVector.Y);
  230. var startExtend = (float)(Math.Ceiling(CalculateDistance(effectiveStart, specifiedStart) / specifiedLength) * specifiedLength);
  231. effectiveStart = MovePointAlongVector(specifiedStart, oppUnitVector, startExtend);
  232. var endExtend = (float)(Math.Ceiling(CalculateDistance(effectiveEnd, specifiedEnd) / specifiedLength) * specifiedLength);
  233. effectiveEnd = MovePointAlongVector(specifiedEnd, specifiedUnitVector, endExtend);
  234. break;
  235. }
  236. return new GradientPoints(effectiveStart, effectiveEnd);
  237. }
  238. private IList<PointF> CandidateIntersections(RectangleF bounds, PointF p1, PointF p2)
  239. {
  240. var results = new List<PointF>();
  241. if (Math.Round(Math.Abs(p1.Y - p2.Y), 4) == 0)
  242. {
  243. results.Add(new PointF(bounds.Left, p1.Y));
  244. results.Add(new PointF(bounds.Right, p1.Y));
  245. }
  246. else if (Math.Round(Math.Abs(p1.X - p2.X), 4) == 0)
  247. {
  248. results.Add(new PointF(p1.X, bounds.Top));
  249. results.Add(new PointF(p1.X, bounds.Bottom));
  250. }
  251. else
  252. {
  253. PointF candidate;
  254. // Save some effort and duplication in the trivial case
  255. if ((p1.X == bounds.Left || p1.X == bounds.Right) && (p1.Y == bounds.Top || p1.Y == bounds.Bottom))
  256. {
  257. results.Add(p1);
  258. }
  259. else
  260. {
  261. candidate = new PointF(bounds.Left, (p2.Y - p1.Y) / (p2.X - p1.X) * (bounds.Left - p1.X) + p1.Y);
  262. if (bounds.Top <= candidate.Y && candidate.Y <= bounds.Bottom) results.Add(candidate);
  263. candidate = new PointF(bounds.Right, (p2.Y - p1.Y) / (p2.X - p1.X) * (bounds.Right - p1.X) + p1.Y);
  264. if (bounds.Top <= candidate.Y && candidate.Y <= bounds.Bottom) results.Add(candidate);
  265. }
  266. if ((p2.X == bounds.Left || p2.X == bounds.Right) && (p2.Y == bounds.Top || p2.Y == bounds.Bottom))
  267. {
  268. results.Add(p2);
  269. }
  270. else
  271. {
  272. candidate = new PointF((bounds.Top - p1.Y) / (p2.Y - p1.Y) * (p2.X - p1.X) + p1.X, bounds.Top);
  273. if (bounds.Left <= candidate.X && candidate.X <= bounds.Right) results.Add(candidate);
  274. candidate = new PointF((bounds.Bottom - p1.Y) / (p2.Y - p1.Y) * (p2.X - p1.X) + p1.X, bounds.Bottom);
  275. if (bounds.Left <= candidate.X && candidate.X <= bounds.Right) results.Add(candidate);
  276. }
  277. }
  278. return results;
  279. }
  280. private ColorBlend CalculateColorBlend(ISvgRenderer renderer, float opacity, PointF specifiedStart, PointF effectiveStart, PointF specifiedEnd, PointF effectiveEnd)
  281. {
  282. float startExtend;
  283. float endExtend;
  284. List<Color> colors;
  285. List<float> positions;
  286. var colorBlend = GetColorBlend(renderer, opacity, false);
  287. var startDelta = CalculateDistance(specifiedStart, effectiveStart);
  288. var endDelta = CalculateDistance(specifiedEnd, effectiveEnd);
  289. if (!(startDelta > 0) && !(endDelta > 0))
  290. {
  291. return colorBlend;
  292. }
  293. var specifiedLength = CalculateDistance(specifiedStart, specifiedEnd);
  294. var specifiedUnitVector = new PointF((specifiedEnd.X - specifiedStart.X) / (float)specifiedLength, (specifiedEnd.Y - specifiedStart.Y) / (float)specifiedLength);
  295. var effectiveLength = CalculateDistance(effectiveStart, effectiveEnd);
  296. switch (SpreadMethod)
  297. {
  298. case SvgGradientSpreadMethod.Reflect:
  299. startExtend = (float)(Math.Ceiling(CalculateDistance(effectiveStart, specifiedStart) / specifiedLength));
  300. endExtend = (float)(Math.Ceiling(CalculateDistance(effectiveEnd, specifiedEnd) / specifiedLength));
  301. colors = colorBlend.Colors.ToList();
  302. positions = (from p in colorBlend.Positions select p + startExtend).ToList();
  303. for (var i = 0; i < startExtend; i++)
  304. {
  305. if (i % 2 == 0)
  306. {
  307. for (var j = 1; j < colorBlend.Positions.Length; j++)
  308. {
  309. positions.Insert(0, (float)((startExtend - 1 - i) + 1 - colorBlend.Positions[j]));
  310. colors.Insert(0, colorBlend.Colors[j]);
  311. }
  312. }
  313. else
  314. {
  315. for (var j = 0; j < colorBlend.Positions.Length - 1; j++)
  316. {
  317. positions.Insert(j, (float)((startExtend - 1 - i) + colorBlend.Positions[j]));
  318. colors.Insert(j, colorBlend.Colors[j]);
  319. }
  320. }
  321. }
  322. int insertPos;
  323. for (var i = 0; i < endExtend; i++)
  324. {
  325. if (i % 2 == 0)
  326. {
  327. insertPos = positions.Count;
  328. for (var j = 0; j < colorBlend.Positions.Length - 1; j++)
  329. {
  330. positions.Insert(insertPos, (float)((startExtend + 1 + i) + 1 - colorBlend.Positions[j]));
  331. colors.Insert(insertPos, colorBlend.Colors[j]);
  332. }
  333. }
  334. else
  335. {
  336. for (var j = 1; j < colorBlend.Positions.Length; j++)
  337. {
  338. positions.Add((float)((startExtend + 1 + i) + colorBlend.Positions[j]));
  339. colors.Add(colorBlend.Colors[j]);
  340. }
  341. }
  342. }
  343. colorBlend.Colors = colors.ToArray();
  344. colorBlend.Positions = (from p in positions select p / (startExtend + 1 + endExtend)).ToArray();
  345. break;
  346. case SvgGradientSpreadMethod.Repeat:
  347. startExtend = (float)(Math.Ceiling(CalculateDistance(effectiveStart, specifiedStart) / specifiedLength));
  348. endExtend = (float)(Math.Ceiling(CalculateDistance(effectiveEnd, specifiedEnd) / specifiedLength));
  349. colors = new List<Color>();
  350. positions = new List<float>();
  351. for (int i = 0; i < startExtend + endExtend + 1; i++)
  352. {
  353. for (int j = 0; j < colorBlend.Positions.Length; j++)
  354. {
  355. positions.Add((i + colorBlend.Positions[j] * 0.9999f) / (startExtend + endExtend + 1));
  356. colors.Add(colorBlend.Colors[j]);
  357. }
  358. }
  359. positions[positions.Count - 1] = 1.0f;
  360. colorBlend.Colors = colors.ToArray();
  361. colorBlend.Positions = positions.ToArray();
  362. break;
  363. default:
  364. for (var i = 0; i < colorBlend.Positions.Length; i++)
  365. {
  366. var originalPoint = MovePointAlongVector(specifiedStart, specifiedUnitVector, (float)specifiedLength * colorBlend.Positions[i]);
  367. var distanceFromEffectiveStart = CalculateDistance(effectiveStart, originalPoint);
  368. colorBlend.Positions[i] = (float)Math.Round(Math.Max(0F, Math.Min((distanceFromEffectiveStart / effectiveLength), 1.0F)), 5);
  369. }
  370. if (startDelta > 0)
  371. {
  372. colorBlend.Positions = new[] { 0F }.Concat(colorBlend.Positions).ToArray();
  373. colorBlend.Colors = new[] { colorBlend.Colors.First() }.Concat(colorBlend.Colors).ToArray();
  374. }
  375. if (endDelta > 0)
  376. {
  377. colorBlend.Positions = colorBlend.Positions.Concat(new[] { 1F }).ToArray();
  378. colorBlend.Colors = colorBlend.Colors.Concat(new[] { colorBlend.Colors.Last() }).ToArray();
  379. }
  380. break;
  381. }
  382. return colorBlend;
  383. }
  384. private static PointF CalculateClosestIntersectionPoint(PointF sourcePoint, IList<PointF> targetPoints)
  385. {
  386. Debug.Assert(targetPoints.Count == 2, "Unexpected number of intersection points!");
  387. return CalculateDistance(sourcePoint, targetPoints[0]) < CalculateDistance(sourcePoint, targetPoints[1]) ? targetPoints[0] : targetPoints[1];
  388. }
  389. private static PointF MovePointAlongVector(PointF start, PointF unitVector, float distance)
  390. {
  391. return start + new SizeF(unitVector.X * distance, unitVector.Y * distance);
  392. }
  393. public override SvgElement DeepCopy()
  394. {
  395. return DeepCopy<SvgLinearGradientServer>();
  396. }
  397. public override SvgElement DeepCopy<T>()
  398. {
  399. var newObj = base.DeepCopy<T>() as SvgLinearGradientServer;
  400. newObj.X1 = this.X1;
  401. newObj.Y1 = this.Y1;
  402. newObj.X2 = this.X2;
  403. newObj.Y2 = this.Y2;
  404. return newObj;
  405. }
  406. private sealed class LineF
  407. {
  408. private float X1
  409. {
  410. get;
  411. set;
  412. }
  413. private float Y1
  414. {
  415. get;
  416. set;
  417. }
  418. private float X2
  419. {
  420. get;
  421. set;
  422. }
  423. private float Y2
  424. {
  425. get;
  426. set;
  427. }
  428. public LineF(float x1, float y1, float x2, float y2)
  429. {
  430. X1 = x1;
  431. Y1 = y1;
  432. X2 = x2;
  433. Y2 = y2;
  434. }
  435. public List<PointF> Intersection(RectangleF rectangle)
  436. {
  437. var result = new List<PointF>();
  438. AddIfIntersect(this, new LineF(rectangle.X, rectangle.Y, rectangle.Right, rectangle.Y), result);
  439. AddIfIntersect(this, new LineF(rectangle.Right, rectangle.Y, rectangle.Right, rectangle.Bottom), result);
  440. AddIfIntersect(this, new LineF(rectangle.Right, rectangle.Bottom, rectangle.X, rectangle.Bottom), result);
  441. AddIfIntersect(this, new LineF(rectangle.X, rectangle.Bottom, rectangle.X, rectangle.Y), result);
  442. return result;
  443. }
  444. /// <remarks>http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=geometry2</remarks>
  445. private PointF? Intersection(LineF other)
  446. {
  447. const int precision = 8;
  448. var a1 = (double)Y2 - Y1;
  449. var b1 = (double)X1 - X2;
  450. var c1 = a1 * X1 + b1 * Y1;
  451. var a2 = (double)other.Y2 - other.Y1;
  452. var b2 = (double)other.X1 - other.X2;
  453. var c2 = a2 * other.X1 + b2 * other.Y1;
  454. var det = a1 * b2 - a2 * b1;
  455. if (det == 0)
  456. {
  457. return null;
  458. }
  459. else
  460. {
  461. var xi = (b2 * c1 - b1 * c2) / det;
  462. var yi = (a1 * c2 - a2 * c1) / det;
  463. if (Math.Round(Math.Min(X1, X2), precision) <= Math.Round(xi, precision) &&
  464. Math.Round(xi, precision) <= Math.Round(Math.Max(X1, X2), precision) &&
  465. Math.Round(Math.Min(Y1, Y2), precision) <= Math.Round(yi, precision) &&
  466. Math.Round(yi, precision) <= Math.Round(Math.Max(Y1, Y2), precision) &&
  467. Math.Round(Math.Min(other.X1, other.X2), precision) <= Math.Round(xi, precision) &&
  468. Math.Round(xi, precision) <= Math.Round(Math.Max(other.X1, other.X2), precision) &&
  469. Math.Round(Math.Min(other.Y1, other.Y2), precision) <= Math.Round(yi, precision) &&
  470. Math.Round(yi, precision) <= Math.Round(Math.Max(other.Y1, other.Y2), precision))
  471. {
  472. return new PointF((float)xi, (float)yi);
  473. }
  474. else
  475. {
  476. return null;
  477. }
  478. }
  479. }
  480. private static void AddIfIntersect(LineF first, LineF second, ICollection<PointF> result)
  481. {
  482. var intersection = first.Intersection(second);
  483. if (intersection != null)
  484. {
  485. result.Add(intersection.Value);
  486. }
  487. }
  488. }
  489. }
  490. }
  491. #pragma warning restore