123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427 |
- using System;
- using System.ComponentModel;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Text;
- using System.Drawing;
- using System.Drawing.Drawing2D;
- using FastReport.Utils;
- using System.IO;
- namespace FastReport.Map
- {
- /// <summary>
- /// Represents a polygon shape.
- /// </summary>
- public partial class ShapePolygon : ShapeBase
- {
- #region Fields
- private BoundingBox box;
- private List<PointD[]> parts;
- private int blobIndex;
- #endregion // Fields
- #region Properties
- /// <summary>
- /// Holds the largest bounding rectangle of this shape.
- /// </summary>
- internal protected RectangleF largestBoundsRect;
- /// <summary>
- /// Gets or sets a bounding box of this shape.
- /// </summary>
- [Browsable(false)]
- public BoundingBox Box
- {
- get { return box; }
- set { box = value; }
- }
- /// <summary>
- /// Gets or sets a list of polygons in this shape.
- /// </summary>
- [Browsable(false)]
- public List<PointD[]> Parts
- {
- get { return parts; }
- set { parts = value; }
- }
- /// <summary>
- /// Gets or sets the shape data in binary format.
- /// </summary>
- [Browsable(false)]
- public byte[] ShapeData
- {
- get
- {
- using (MemoryStream stream = new MemoryStream())
- {
- Save(stream);
- return stream.ToArray();
- }
- }
- set
- {
- using (MemoryStream stream = new MemoryStream(value))
- {
- Load(stream);
- }
- }
- }
- #endregion // Properties
- #region Private Methods
- private void ResetBlobIndex()
- {
- blobIndex = -1;
- }
- #endregion
- #region Public Methods
- internal RectangleF GetBBox()
- {
- double minX = Layer.Box.MinX;
- double maxY = Layer.Box.MaxY;
- float scale = Map.ScaleG;
- double w2 = Box.MinX + (Box.MaxX - Box.MinX) / 2;
- double h2 = Box.MinY + (Box.MaxY - Box.MinY) / 2;
- double addx = -w2 * ShapeScale + w2 + ShapeOffsetX;
- double addy = -h2 * ShapeScale + h2 + ShapeOffsetY;
- float left = CoordinateConverter.GetX(Box.MinX * ShapeScale + addx, minX, scale);
- float top = CoordinateConverter.GetY(Box.MaxY * ShapeScale + addy, maxY, scale, Map.MercatorProjection);
- float right = CoordinateConverter.GetX(Box.MaxX * ShapeScale + addx, minX, scale);
- float bottom = CoordinateConverter.GetY(Box.MinY * ShapeScale + addy, maxY, scale, Map.MercatorProjection);
- return new RectangleF(
- Map.AbsLeft + Map.Padding.Left + Map.OffsetXG + left,
- Map.AbsTop + Map.Padding.Top + Map.OffsetYG + top,
- right - left,
- bottom - top);
- }
- internal bool IsVisible()
- {
- RectangleF polygonRect = GetBBox();
- if (polygonRect.Right < Map.AbsLeft || polygonRect.Bottom < Map.AbsTop ||
- polygonRect.Left > Map.AbsRight || polygonRect.Top > Map.AbsBottom)
- return false;
- return true;
- }
- internal double DistanceBetweenPoints(PointF pt1, PointF pt2)
- {
- return Math.Sqrt((pt1.X - pt2.X) * (pt1.X - pt2.X) + (pt1.Y - pt2.Y) * (pt1.Y - pt2.Y));
- }
- internal double DistanceBetweenPoints(PointD pt1, PointD pt2)
- {
- return Math.Sqrt((pt1.X - pt2.X) * (pt1.X - pt2.X) + (pt1.Y - pt2.Y) * (pt1.Y - pt2.Y));
- }
- internal GraphicsPath GetGraphicsPath(FRPaintEventArgs e)
- {
- float left = Map.AbsLeft + Map.Padding.Left + Map.OffsetXG;
- float top = Map.AbsTop + Map.Padding.Top + Map.OffsetYG;
- double minX = Layer.Box.MinX;
- double maxY = Layer.Box.MaxY;
- double w2 = Box.MinX + (Box.MaxX - Box.MinX) / 2;
- double h2 = Box.MinY + (Box.MaxY - Box.MinY) / 2;
- double addx = -w2 * ShapeScale + w2 + ShapeOffsetX;
- double addy = -h2 * ShapeScale + h2 + ShapeOffsetY;
- float scale = Map.ScaleG;
- largestBoundsRect = new RectangleF();
- GraphicsPath path = new GraphicsPath();
- foreach (PointD[] part in Parts)
- {
- float polyLeft = 1e6f;
- float polyTop = 1e6f;
- float polyRight = -1e6f;
- float polyBottom = -1e6f;
- List<PointF> points = new List<PointF>();
- foreach (PointD point in part)
- {
- PointF pt = new PointF(
- (left + CoordinateConverter.GetX(point.X * ShapeScale + addx, minX, scale)) * e.ScaleX,
- (top + CoordinateConverter.GetY(point.Y * ShapeScale + addy, maxY, scale, Map.MercatorProjection)) * e.ScaleY);
- if (points.Count > 0 && DistanceBetweenPoints(pt, points[points.Count - 1]) < Layer.Accuracy)
- continue;
- points.Add(pt);
- if (pt.X < polyLeft)
- polyLeft = pt.X;
- if (pt.X > polyRight)
- polyRight = pt.X;
- if (pt.Y < polyTop)
- polyTop = pt.Y;
- if (pt.Y > polyBottom)
- polyBottom = pt.Y;
- }
- if (points.Count > 2)
- {
- path.AddPolygon(points.ToArray());
- if (polyRight - polyLeft > largestBoundsRect.Width)
- largestBoundsRect = new RectangleF(polyLeft, polyTop, polyRight - polyLeft, polyBottom - polyTop);
- }
- }
- return path;
- }
- internal void Load(Stream stream)
- {
- Box.Load(stream);
- byte[] buffer4 = new byte[4];
- stream.Read(buffer4, 0, buffer4.Length);
- int numParts = BitConverter.ToInt32(buffer4, 0);
- stream.Read(buffer4, 0, buffer4.Length);
- int numPoints = BitConverter.ToInt32(buffer4, 0);
- int[] parts = new int[numParts + 1];
- for (int i = 0; i < numParts; i++)
- {
- stream.Read(buffer4, 0, buffer4.Length);
- parts[i] = BitConverter.ToInt32(buffer4, 0);
- }
- parts[numParts] = numPoints;
- Parts.Clear();
- for (int i = 0; i < numParts; i++)
- {
- int pointsInPart = parts[i + 1] - parts[i];
- PointD[] part = new PointD[pointsInPart];
- Parts.Add(part);
- for (int j = 0; j < pointsInPart; j++)
- {
- PointD point = new PointD();
- point.Load(stream);
- part[j] = point;
- }
- }
- }
- internal void Save(Stream stream)
- {
- Box.Save(stream);
- int numParts = Parts.Count;
- byte[] buffer4 = BitConverter.GetBytes(numParts);
- stream.Write(buffer4, 0, buffer4.Length);
- int numPoints = 0;
- foreach (PointD[] part in Parts)
- {
- numPoints += part.Length;
- }
- buffer4 = BitConverter.GetBytes(numPoints);
- stream.Write(buffer4, 0, buffer4.Length);
- numPoints = 0;
- foreach (PointD[] part in Parts)
- {
- buffer4 = BitConverter.GetBytes(numPoints);
- stream.Write(buffer4, 0, buffer4.Length);
- numPoints += part.Length;
- }
- foreach (PointD[] part in Parts)
- {
- foreach (PointD point in part)
- {
- point.Save(stream);
- }
- }
- }
- /// <inheritdoc/>
- public override void Assign(Base source)
- {
- base.Assign(source);
- ShapePolygon src = source as ShapePolygon;
- Box.Assign(src.Box);
- ShapeData = src.ShapeData;
- }
- /// <inheritdoc/>
- public override void Draw(FRPaintEventArgs e)
- {
- if (!IsVisible())
- return;
- ShapeStyle style = UseCustomStyle ? CustomStyle : Layer.DefaultShapeStyle;
- Pen pen = e.Cache.GetPen(style.BorderColor, style.BorderWidth * e.ScaleX, style.BorderStyle, LineJoin.Bevel);
- Color fillColor = style.FillColor;
- if (Layer.Palette != MapPalette.None && !UseCustomStyle)
- fillColor = ColorPalette.GetColor(ShapeIndex, Layer.Palette);
- Brush brush = e.Cache.GetBrush(fillColor);
- bool disposeBrush = false;
- // get color from the layer's color range
- if (!IsValueEmpty && Layer.ColorRanges.RangeCount > 0)
- brush = e.Cache.GetBrush(Layer.ColorRanges.GetColor(Value));
- DrawDesign(e, ref brush, ref disposeBrush);
- using (GraphicsPath path = GetGraphicsPath(e))
- {
- if (path.PointCount > 0)
- {
- e.Graphics.FillAndDrawPath(pen, brush, path);
- }
- }
- if (disposeBrush)
- brush.Dispose();
- }
- /// <inheritdoc/>
- public override void DrawLabel(FRPaintEventArgs e)
- {
- if (!IsVisible())
- return;
- ShapeStyle style = UseCustomStyle ? CustomStyle : Layer.DefaultShapeStyle;
- string text = "";
- if (Layer.LabelKind != MapLabelKind.Value)
- text = SpatialData.GetValue(Layer.LabelColumn);
- if (!IsValueEmpty)
- {
- if (Layer.LabelKind == MapLabelKind.NameAndValue || Layer.LabelKind == MapLabelKind.Value)
- {
- try
- {
- text += "\r\n" + Value.ToString(Layer.LabelFormat);
- }
- catch
- {
- Layer.LabelFormat = "";
- text += "\r\n" + Value.ToString();
- }
- }
- }
- Font font = e.Cache.GetFont(style.Font.FontFamily,
- IsPrinting ? style.Font.Size : style.Font.Size * e.ScaleX * 96f / DrawUtils.ScreenDpi,
- style.Font.Style);
- float width = e.Graphics.MeasureString(text, font).Width;
- if (width < largestBoundsRect.Width)
- {
- RectangleF textBounds = largestBoundsRect;
- textBounds.Offset(CenterOffsetX * e.ScaleX, CenterOffsetY * e.ScaleY);
- StringFormat format = e.Cache.GetStringFormat(StringAlignment.Center, StringAlignment.Center, StringTrimming.None, StringFormatFlags.NoClip, 0, 0);
- SolidBrush brush = e.Cache.GetBrush(style.TextColor);
- e.Graphics.DrawString(text, font, brush, textBounds, format);
- }
- }
- /// <inheritdoc/>
- public override bool HitTest(PointF point)
- {
- // first check BBox, it's fast
- if (GetBBox().Contains(point))
- {
- // check graphics path
- GraphicsPath path = GetGraphicsPath(new FRPaintEventArgs((IGraphics)null, 1, 1, null));
- if (path != null)
- {
- bool ok = path.IsVisible(point);
- path.Dispose();
- if (ok)
- return true;
- }
- }
- return false;
- }
- /// <inheritdoc/>
- public override void Simplify(double accuracy)
- {
- for (int i = 0; i < Parts.Count; i++)
- {
- PointD[] part = Parts[i];
- List<PointD> points = new List<PointD>();
- foreach (PointD point in part)
- {
- if (points.Count > 0 && DistanceBetweenPoints(point, points[points.Count - 1]) < accuracy)
- continue;
- points.Add(point);
- }
- if (points.Count > 2)
- Parts[i] = points.ToArray();
- else
- {
- Parts.Remove(part);
- i--;
- }
- }
- }
- /// <inheritdoc/>
- public override void Serialize(FRWriter writer)
- {
- base.Serialize(writer);
- if (writer.SerializeTo != SerializeTo.SourcePages)
- {
- if (writer.BlobStore != null)
- {
- if (blobIndex == -1 || blobIndex >= writer.BlobStore.Count)
- blobIndex = writer.BlobStore.Add(ShapeData);
- writer.WriteInt("BlobIndex", blobIndex);
- }
- else
- {
- if (Parts.Count > 0)
- writer.WriteValue("ShapeData", ShapeData);
- }
- }
- }
- /// <inheritdoc/>
- public override void Deserialize(FRReader reader)
- {
- base.Deserialize(reader);
- if (reader.HasProperty("BlobIndex"))
- {
- blobIndex = reader.ReadInt("BlobIndex");
- if (reader.BlobStore != null && blobIndex != -1)
- ShapeData = reader.BlobStore.Get(blobIndex);
- }
- }
- /// <inheritdoc/>
- public override void InitializeComponent()
- {
- ResetBlobIndex();
- }
- /// <inheritdoc/>
- public override void FinalizeComponent()
- {
- ResetBlobIndex();
- }
- #endregion // Public Methods
- /// <summary>
- /// Initializes a new instance of the <see cref="ShapePolygon"/> class.
- /// </summary>
- public ShapePolygon()
- {
- box = new BoundingBox();
- parts = new List<PointD[]>();
- blobIndex = -1;
- BaseName = "Polygon";
- }
- }
- }
|