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 { /// /// Represents a polygon shape. /// public partial class ShapePolygon : ShapeBase { #region Fields private BoundingBox box; private List parts; private int blobIndex; #endregion // Fields #region Properties /// /// Holds the largest bounding rectangle of this shape. /// internal protected RectangleF largestBoundsRect; /// /// Gets or sets a bounding box of this shape. /// [Browsable(false)] public BoundingBox Box { get { return box; } set { box = value; } } /// /// Gets or sets a list of polygons in this shape. /// [Browsable(false)] public List Parts { get { return parts; } set { parts = value; } } /// /// Gets or sets the shape data in binary format. /// [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 points = new List(); 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); } } } /// public override void Assign(Base source) { base.Assign(source); ShapePolygon src = source as ShapePolygon; Box.Assign(src.Box); ShapeData = src.ShapeData; } /// 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(); } /// 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); } } /// 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; } /// public override void Simplify(double accuracy) { for (int i = 0; i < Parts.Count; i++) { PointD[] part = Parts[i]; List points = new List(); 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--; } } } /// 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); } } } /// 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); } } /// public override void InitializeComponent() { ResetBlobIndex(); } /// public override void FinalizeComponent() { ResetBlobIndex(); } #endregion // Public Methods /// /// Initializes a new instance of the class. /// public ShapePolygon() { box = new BoundingBox(); parts = new List(); blobIndex = -1; BaseName = "Polygon"; } } }