ShapePolygon.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. using System;
  2. using System.ComponentModel;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Text;
  6. using System.Drawing;
  7. using System.Drawing.Drawing2D;
  8. using FastReport.Utils;
  9. using System.IO;
  10. namespace FastReport.Map
  11. {
  12. /// <summary>
  13. /// Represents a polygon shape.
  14. /// </summary>
  15. public partial class ShapePolygon : ShapeBase
  16. {
  17. #region Fields
  18. private BoundingBox box;
  19. private List<PointD[]> parts;
  20. private int blobIndex;
  21. #endregion // Fields
  22. #region Properties
  23. /// <summary>
  24. /// Holds the largest bounding rectangle of this shape.
  25. /// </summary>
  26. internal protected RectangleF largestBoundsRect;
  27. /// <summary>
  28. /// Gets or sets a bounding box of this shape.
  29. /// </summary>
  30. [Browsable(false)]
  31. public BoundingBox Box
  32. {
  33. get { return box; }
  34. set { box = value; }
  35. }
  36. /// <summary>
  37. /// Gets or sets a list of polygons in this shape.
  38. /// </summary>
  39. [Browsable(false)]
  40. public List<PointD[]> Parts
  41. {
  42. get { return parts; }
  43. set { parts = value; }
  44. }
  45. /// <summary>
  46. /// Gets or sets the shape data in binary format.
  47. /// </summary>
  48. [Browsable(false)]
  49. public byte[] ShapeData
  50. {
  51. get
  52. {
  53. using (MemoryStream stream = new MemoryStream())
  54. {
  55. Save(stream);
  56. return stream.ToArray();
  57. }
  58. }
  59. set
  60. {
  61. using (MemoryStream stream = new MemoryStream(value))
  62. {
  63. Load(stream);
  64. }
  65. }
  66. }
  67. #endregion // Properties
  68. #region Private Methods
  69. private void ResetBlobIndex()
  70. {
  71. blobIndex = -1;
  72. }
  73. #endregion
  74. #region Public Methods
  75. internal RectangleF GetBBox()
  76. {
  77. double minX = Layer.Box.MinX;
  78. double maxY = Layer.Box.MaxY;
  79. float scale = Map.ScaleG;
  80. double w2 = Box.MinX + (Box.MaxX - Box.MinX) / 2;
  81. double h2 = Box.MinY + (Box.MaxY - Box.MinY) / 2;
  82. double addx = -w2 * ShapeScale + w2 + ShapeOffsetX;
  83. double addy = -h2 * ShapeScale + h2 + ShapeOffsetY;
  84. float left = CoordinateConverter.GetX(Box.MinX * ShapeScale + addx, minX, scale);
  85. float top = CoordinateConverter.GetY(Box.MaxY * ShapeScale + addy, maxY, scale, Map.MercatorProjection);
  86. float right = CoordinateConverter.GetX(Box.MaxX * ShapeScale + addx, minX, scale);
  87. float bottom = CoordinateConverter.GetY(Box.MinY * ShapeScale + addy, maxY, scale, Map.MercatorProjection);
  88. return new RectangleF(
  89. Map.AbsLeft + Map.Padding.Left + Map.OffsetXG + left,
  90. Map.AbsTop + Map.Padding.Top + Map.OffsetYG + top,
  91. right - left,
  92. bottom - top);
  93. }
  94. internal bool IsVisible()
  95. {
  96. RectangleF polygonRect = GetBBox();
  97. if (polygonRect.Right < Map.AbsLeft || polygonRect.Bottom < Map.AbsTop ||
  98. polygonRect.Left > Map.AbsRight || polygonRect.Top > Map.AbsBottom)
  99. return false;
  100. return true;
  101. }
  102. internal double DistanceBetweenPoints(PointF pt1, PointF pt2)
  103. {
  104. return Math.Sqrt((pt1.X - pt2.X) * (pt1.X - pt2.X) + (pt1.Y - pt2.Y) * (pt1.Y - pt2.Y));
  105. }
  106. internal double DistanceBetweenPoints(PointD pt1, PointD pt2)
  107. {
  108. return Math.Sqrt((pt1.X - pt2.X) * (pt1.X - pt2.X) + (pt1.Y - pt2.Y) * (pt1.Y - pt2.Y));
  109. }
  110. internal GraphicsPath GetGraphicsPath(FRPaintEventArgs e)
  111. {
  112. float left = Map.AbsLeft + Map.Padding.Left + Map.OffsetXG;
  113. float top = Map.AbsTop + Map.Padding.Top + Map.OffsetYG;
  114. double minX = Layer.Box.MinX;
  115. double maxY = Layer.Box.MaxY;
  116. double w2 = Box.MinX + (Box.MaxX - Box.MinX) / 2;
  117. double h2 = Box.MinY + (Box.MaxY - Box.MinY) / 2;
  118. double addx = -w2 * ShapeScale + w2 + ShapeOffsetX;
  119. double addy = -h2 * ShapeScale + h2 + ShapeOffsetY;
  120. float scale = Map.ScaleG;
  121. largestBoundsRect = new RectangleF();
  122. GraphicsPath path = new GraphicsPath();
  123. foreach (PointD[] part in Parts)
  124. {
  125. float polyLeft = 1e6f;
  126. float polyTop = 1e6f;
  127. float polyRight = -1e6f;
  128. float polyBottom = -1e6f;
  129. List<PointF> points = new List<PointF>();
  130. foreach (PointD point in part)
  131. {
  132. PointF pt = new PointF(
  133. (left + CoordinateConverter.GetX(point.X * ShapeScale + addx, minX, scale)) * e.ScaleX,
  134. (top + CoordinateConverter.GetY(point.Y * ShapeScale + addy, maxY, scale, Map.MercatorProjection)) * e.ScaleY);
  135. if (points.Count > 0 && DistanceBetweenPoints(pt, points[points.Count - 1]) < Layer.Accuracy)
  136. continue;
  137. points.Add(pt);
  138. if (pt.X < polyLeft)
  139. polyLeft = pt.X;
  140. if (pt.X > polyRight)
  141. polyRight = pt.X;
  142. if (pt.Y < polyTop)
  143. polyTop = pt.Y;
  144. if (pt.Y > polyBottom)
  145. polyBottom = pt.Y;
  146. }
  147. if (points.Count > 2)
  148. {
  149. path.AddPolygon(points.ToArray());
  150. if (polyRight - polyLeft > largestBoundsRect.Width)
  151. largestBoundsRect = new RectangleF(polyLeft, polyTop, polyRight - polyLeft, polyBottom - polyTop);
  152. }
  153. }
  154. return path;
  155. }
  156. internal void Load(Stream stream)
  157. {
  158. Box.Load(stream);
  159. byte[] buffer4 = new byte[4];
  160. stream.Read(buffer4, 0, buffer4.Length);
  161. int numParts = BitConverter.ToInt32(buffer4, 0);
  162. stream.Read(buffer4, 0, buffer4.Length);
  163. int numPoints = BitConverter.ToInt32(buffer4, 0);
  164. int[] parts = new int[numParts + 1];
  165. for (int i = 0; i < numParts; i++)
  166. {
  167. stream.Read(buffer4, 0, buffer4.Length);
  168. parts[i] = BitConverter.ToInt32(buffer4, 0);
  169. }
  170. parts[numParts] = numPoints;
  171. Parts.Clear();
  172. for (int i = 0; i < numParts; i++)
  173. {
  174. int pointsInPart = parts[i + 1] - parts[i];
  175. PointD[] part = new PointD[pointsInPart];
  176. Parts.Add(part);
  177. for (int j = 0; j < pointsInPart; j++)
  178. {
  179. PointD point = new PointD();
  180. point.Load(stream);
  181. part[j] = point;
  182. }
  183. }
  184. }
  185. internal void Save(Stream stream)
  186. {
  187. Box.Save(stream);
  188. int numParts = Parts.Count;
  189. byte[] buffer4 = BitConverter.GetBytes(numParts);
  190. stream.Write(buffer4, 0, buffer4.Length);
  191. int numPoints = 0;
  192. foreach (PointD[] part in Parts)
  193. {
  194. numPoints += part.Length;
  195. }
  196. buffer4 = BitConverter.GetBytes(numPoints);
  197. stream.Write(buffer4, 0, buffer4.Length);
  198. numPoints = 0;
  199. foreach (PointD[] part in Parts)
  200. {
  201. buffer4 = BitConverter.GetBytes(numPoints);
  202. stream.Write(buffer4, 0, buffer4.Length);
  203. numPoints += part.Length;
  204. }
  205. foreach (PointD[] part in Parts)
  206. {
  207. foreach (PointD point in part)
  208. {
  209. point.Save(stream);
  210. }
  211. }
  212. }
  213. /// <inheritdoc/>
  214. public override void Assign(Base source)
  215. {
  216. base.Assign(source);
  217. ShapePolygon src = source as ShapePolygon;
  218. Box.Assign(src.Box);
  219. ShapeData = src.ShapeData;
  220. }
  221. /// <inheritdoc/>
  222. public override void Draw(FRPaintEventArgs e)
  223. {
  224. if (!IsVisible())
  225. return;
  226. ShapeStyle style = UseCustomStyle ? CustomStyle : Layer.DefaultShapeStyle;
  227. Pen pen = e.Cache.GetPen(style.BorderColor, style.BorderWidth * e.ScaleX, style.BorderStyle, LineJoin.Bevel);
  228. Color fillColor = style.FillColor;
  229. if (Layer.Palette != MapPalette.None && !UseCustomStyle)
  230. fillColor = ColorPalette.GetColor(ShapeIndex, Layer.Palette);
  231. Brush brush = e.Cache.GetBrush(fillColor);
  232. bool disposeBrush = false;
  233. // get color from the layer's color range
  234. if (!IsValueEmpty && Layer.ColorRanges.RangeCount > 0)
  235. brush = e.Cache.GetBrush(Layer.ColorRanges.GetColor(Value));
  236. DrawDesign(e, ref brush, ref disposeBrush);
  237. using (GraphicsPath path = GetGraphicsPath(e))
  238. {
  239. if (path.PointCount > 0)
  240. {
  241. e.Graphics.FillAndDrawPath(pen, brush, path);
  242. }
  243. }
  244. if (disposeBrush)
  245. brush.Dispose();
  246. }
  247. /// <inheritdoc/>
  248. public override void DrawLabel(FRPaintEventArgs e)
  249. {
  250. if (!IsVisible())
  251. return;
  252. ShapeStyle style = UseCustomStyle ? CustomStyle : Layer.DefaultShapeStyle;
  253. string text = "";
  254. if (Layer.LabelKind != MapLabelKind.Value)
  255. text = SpatialData.GetValue(Layer.LabelColumn);
  256. if (!IsValueEmpty)
  257. {
  258. if (Layer.LabelKind == MapLabelKind.NameAndValue || Layer.LabelKind == MapLabelKind.Value)
  259. {
  260. try
  261. {
  262. text += "\r\n" + Value.ToString(Layer.LabelFormat);
  263. }
  264. catch
  265. {
  266. Layer.LabelFormat = "";
  267. text += "\r\n" + Value.ToString();
  268. }
  269. }
  270. }
  271. Font font = e.Cache.GetFont(style.Font.FontFamily,
  272. IsPrinting ? style.Font.Size : style.Font.Size * e.ScaleX * 96f / DrawUtils.ScreenDpi,
  273. style.Font.Style);
  274. float width = e.Graphics.MeasureString(text, font).Width;
  275. if (width < largestBoundsRect.Width)
  276. {
  277. RectangleF textBounds = largestBoundsRect;
  278. textBounds.Offset(CenterOffsetX * e.ScaleX, CenterOffsetY * e.ScaleY);
  279. StringFormat format = e.Cache.GetStringFormat(StringAlignment.Center, StringAlignment.Center, StringTrimming.None, StringFormatFlags.NoClip, 0, 0);
  280. SolidBrush brush = e.Cache.GetBrush(style.TextColor);
  281. e.Graphics.DrawString(text, font, brush, textBounds, format);
  282. }
  283. }
  284. /// <inheritdoc/>
  285. public override bool HitTest(PointF point)
  286. {
  287. // first check BBox, it's fast
  288. if (GetBBox().Contains(point))
  289. {
  290. // check graphics path
  291. GraphicsPath path = GetGraphicsPath(new FRPaintEventArgs((IGraphics)null, 1, 1, null));
  292. if (path != null)
  293. {
  294. bool ok = path.IsVisible(point);
  295. path.Dispose();
  296. if (ok)
  297. return true;
  298. }
  299. }
  300. return false;
  301. }
  302. /// <inheritdoc/>
  303. public override void Simplify(double accuracy)
  304. {
  305. for (int i = 0; i < Parts.Count; i++)
  306. {
  307. PointD[] part = Parts[i];
  308. List<PointD> points = new List<PointD>();
  309. foreach (PointD point in part)
  310. {
  311. if (points.Count > 0 && DistanceBetweenPoints(point, points[points.Count - 1]) < accuracy)
  312. continue;
  313. points.Add(point);
  314. }
  315. if (points.Count > 2)
  316. Parts[i] = points.ToArray();
  317. else
  318. {
  319. Parts.Remove(part);
  320. i--;
  321. }
  322. }
  323. }
  324. /// <inheritdoc/>
  325. public override void Serialize(FRWriter writer)
  326. {
  327. base.Serialize(writer);
  328. if (writer.SerializeTo != SerializeTo.SourcePages)
  329. {
  330. if (writer.BlobStore != null)
  331. {
  332. if (blobIndex == -1 || blobIndex >= writer.BlobStore.Count)
  333. blobIndex = writer.BlobStore.Add(ShapeData);
  334. writer.WriteInt("BlobIndex", blobIndex);
  335. }
  336. else
  337. {
  338. if (Parts.Count > 0)
  339. writer.WriteValue("ShapeData", ShapeData);
  340. }
  341. }
  342. }
  343. /// <inheritdoc/>
  344. public override void Deserialize(FRReader reader)
  345. {
  346. base.Deserialize(reader);
  347. if (reader.HasProperty("BlobIndex"))
  348. {
  349. blobIndex = reader.ReadInt("BlobIndex");
  350. if (reader.BlobStore != null && blobIndex != -1)
  351. ShapeData = reader.BlobStore.Get(blobIndex);
  352. }
  353. }
  354. /// <inheritdoc/>
  355. public override void InitializeComponent()
  356. {
  357. ResetBlobIndex();
  358. }
  359. /// <inheritdoc/>
  360. public override void FinalizeComponent()
  361. {
  362. ResetBlobIndex();
  363. }
  364. #endregion // Public Methods
  365. /// <summary>
  366. /// Initializes a new instance of the <see cref="ShapePolygon"/> class.
  367. /// </summary>
  368. public ShapePolygon()
  369. {
  370. box = new BoundingBox();
  371. parts = new List<PointD[]>();
  372. blobIndex = -1;
  373. BaseName = "Polygon";
  374. }
  375. }
  376. }