/// <summary> /// Gets position of the axis break line. Break line may be shown as a single /// line or two lines separated with a spacing. /// </summary> /// <param name="graph">Chart graphics.</param> /// <param name="nextSegment">Next segment reference.</param> /// <returns>Position of the axis break line in pixel coordinates.</returns> internal RectangleF GetBreakLinePosition(ChartGraphics graph, AxisScaleSegment nextSegment) { // Start with the plotting rectangle position RectangleF breakPosition = this.axis.PlotAreaPosition.ToRectangleF(); // Find maximum scale value of the current segment and minimuj of the next double from = this.axis.GetLinearPosition(nextSegment.ScaleMinimum); double to = this.axis.GetLinearPosition(this.ScaleMaximum); if (this.axis.AxisPosition == AxisPosition.Right || this.axis.AxisPosition == AxisPosition.Left) { breakPosition.Y = (float)Math.Min(from, to); breakPosition.Height = (float)Math.Max(from, to); } else { breakPosition.X = (float)Math.Min(from, to); breakPosition.Width = (float)Math.Max(from, to);; } // Convert to pixels breakPosition = Rectangle.Round(graph.GetAbsoluteRectangle(breakPosition)); // Add border width if (this.axis.AxisPosition == AxisPosition.Right || this.axis.AxisPosition == AxisPosition.Left) { breakPosition.Height = (float)Math.Abs(breakPosition.Y - breakPosition.Height); breakPosition.X -= this.axis.ChartArea.BorderWidth; breakPosition.Width += 2 * this.axis.ChartArea.BorderWidth; } else { breakPosition.Width = (float)Math.Abs(breakPosition.X - breakPosition.Width); breakPosition.Y -= this.axis.ChartArea.BorderWidth; breakPosition.Height += 2 * this.axis.ChartArea.BorderWidth; } return(breakPosition); }
/// <summary> /// Paints the axis break line. /// </summary> /// <param name="graph">Chart graphics to use.</param> /// <param name="nextSegment">Axis scale segment next to current.</param> internal void PaintBreakLine(ChartGraphics graph, AxisScaleSegment nextSegment) { // Get break line position RectangleF breakPosition = this.GetBreakLinePosition(graph, nextSegment); // Get top line graphics path GraphicsPath breakLinePathTop = this.GetBreakLinePath(breakPosition, true); GraphicsPath breakLinePathBottom = null; // Clear break line space using chart color behind the area if (breakPosition.Width > 0f && breakPosition.Height > 0f) { // Get bottom line graphics path breakLinePathBottom = this.GetBreakLinePath(breakPosition, false); // Clear plotting area background using (GraphicsPath fillPath = new GraphicsPath()) { // Create fill path out of top and bottom break lines fillPath.AddPath(breakLinePathTop, true); fillPath.Reverse(); fillPath.AddPath(breakLinePathBottom, true); fillPath.CloseAllFigures(); // Use chart back color to fill the area using (Brush fillBrush = this.GetChartFillBrush(graph)) { graph.FillPath(fillBrush, fillPath); // Check if shadow exsits in chart area if (this.axis.ChartArea.ShadowOffset != 0 && !this.axis.ChartArea.ShadowColor.IsEmpty) { // Clear shadow RectangleF shadowPartRect = breakPosition; if (this.axis.AxisPosition == AxisPosition.Right || this.axis.AxisPosition == AxisPosition.Left) { shadowPartRect.Y += this.axis.ChartArea.ShadowOffset; shadowPartRect.Height -= this.axis.ChartArea.ShadowOffset; shadowPartRect.X = shadowPartRect.Right - 1; shadowPartRect.Width = this.axis.ChartArea.ShadowOffset + 2; } else { shadowPartRect.X += this.axis.ChartArea.ShadowOffset; shadowPartRect.Width -= this.axis.ChartArea.ShadowOffset; shadowPartRect.Y = shadowPartRect.Bottom - 1; shadowPartRect.Height = this.axis.ChartArea.ShadowOffset + 2; } graph.FillRectangle(fillBrush, shadowPartRect); // Draw new shadow using (GraphicsPath shadowPath = new GraphicsPath()) { shadowPath.AddPath(breakLinePathTop, false); // Define maximum size float size = this.axis.ChartArea.ShadowOffset; if (this.axis.AxisPosition == AxisPosition.Right || this.axis.AxisPosition == AxisPosition.Left) { size = Math.Min(size, breakPosition.Height); } else { size = Math.Min(size, breakPosition.Width); } // Define step to increase transperancy int transparencyStep = (int)(this.axis.ChartArea.ShadowColor.A / size); // Set clip region to achieve spacing of the shadow // Start with the plotting rectangle position RectangleF clipRegion = graph.GetAbsoluteRectangle(this.axis.PlotAreaPosition.ToRectangleF()); if (this.axis.AxisPosition == AxisPosition.Right || this.axis.AxisPosition == AxisPosition.Left) { clipRegion.X += this.axis.ChartArea.ShadowOffset; clipRegion.Width += this.axis.ChartArea.ShadowOffset; } else { clipRegion.Y += this.axis.ChartArea.ShadowOffset; clipRegion.Height += this.axis.ChartArea.ShadowOffset; } graph.SetClip(graph.GetRelativeRectangle(clipRegion)); // Draw several lines to form shadow for (int index = 0; index < size; index++) { using (Matrix newMatrix = new Matrix()) { // Shift top break line by 1 pixel if (this.axis.AxisPosition == AxisPosition.Right || this.axis.AxisPosition == AxisPosition.Left) { newMatrix.Translate(0f, 1f); } else { newMatrix.Translate(1f, 0f); } shadowPath.Transform(newMatrix); } // Get line color Color color = Color.FromArgb( this.axis.ChartArea.ShadowColor.A - transparencyStep * index, this.axis.ChartArea.ShadowColor); using (Pen shadowPen = new Pen(color, 1)) { // Draw shadow graph.DrawPath(shadowPen, shadowPath); } } graph.ResetClip(); } } } } } // Draw Separator Line(s) if (this.axis.ScaleBreakStyle.BreakLineStyle != BreakLineStyle.None) { using (Pen pen = new Pen(this.axis.ScaleBreakStyle.LineColor, this.axis.ScaleBreakStyle.LineWidth)) { // Set line style pen.DashStyle = graph.GetPenStyle(this.axis.ScaleBreakStyle.LineDashStyle); // Draw break lines graph.DrawPath(pen, breakLinePathTop); if (breakPosition.Width > 0f && breakPosition.Height > 0f) { graph.DrawPath(pen, breakLinePathBottom); } } } // Dispose break line paths breakLinePathTop.Dispose(); breakLinePathTop = null; if (breakLinePathBottom != null) { breakLinePathBottom.Dispose(); breakLinePathBottom = null; } }
/// <summary> /// Helper method which calculates chart area plotting position in pixels. /// </summary> /// <param name="area">Chart area to get the plotting area position.</param> /// <param name="chartGraphics">Chart graphics object.</param> /// <returns>Chart area ploting area position in pixels.</returns> private RectangleF GetChartAreaPlottingPosition(ChartArea area, ChartGraphics chartGraphics) { RectangleF plottingRect = area.Position.ToRectangleF(); plottingRect.X += (area.Position.Width / 100F) * area.InnerPlotPosition.X; plottingRect.Y += (area.Position.Height / 100F) * area.InnerPlotPosition.Y; plottingRect.Width = (area.Position.Width / 100F) * area.InnerPlotPosition.Width; plottingRect.Height = (area.Position.Height / 100F) * area.InnerPlotPosition.Height; plottingRect = chartGraphics.GetAbsoluteRectangle(plottingRect); return plottingRect; }
/// <summary> /// Get arrow path for the specified annotation position /// </summary> /// <param name="graphics"></param> /// <param name="position"></param> /// <returns></returns> private GraphicsPath GetArrowPath( ChartGraphics graphics, RectangleF position) { // Get absolute position RectangleF positionAbs = graphics.GetAbsoluteRectangle(position); PointF firstPoint = positionAbs.Location; PointF secondPoint = new PointF(positionAbs.Right, positionAbs.Bottom); // Calculate arrow length float deltaX = secondPoint.X - firstPoint.X; float deltaY = secondPoint.Y - firstPoint.Y; float arrowLength = (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY); // Create unrotated graphics path for the arrow started at the annotation location // and going to the right for the length of the rotated arrow. GraphicsPath path = new GraphicsPath(); PointF[] points = null; float pointerRatio = 2.1f; if (this.ArrowStyle == ArrowStyle.Simple) { points = new PointF[] { firstPoint, new PointF(firstPoint.X + this.ArrowSize * pointerRatio, firstPoint.Y - this.ArrowSize * pointerRatio), new PointF(firstPoint.X + this.ArrowSize * pointerRatio, firstPoint.Y - this.ArrowSize), new PointF(firstPoint.X + arrowLength, firstPoint.Y - this.ArrowSize), new PointF(firstPoint.X + arrowLength, firstPoint.Y + this.ArrowSize), new PointF(firstPoint.X + this.ArrowSize * pointerRatio, firstPoint.Y + this.ArrowSize), new PointF(firstPoint.X + this.ArrowSize * pointerRatio, firstPoint.Y + this.ArrowSize * pointerRatio) }; } else if (this.ArrowStyle == ArrowStyle.DoubleArrow) { points = new PointF[] { firstPoint, new PointF(firstPoint.X + this.ArrowSize * pointerRatio, firstPoint.Y - this.ArrowSize * pointerRatio), new PointF(firstPoint.X + this.ArrowSize * pointerRatio, firstPoint.Y - this.ArrowSize), new PointF(firstPoint.X + arrowLength - this.ArrowSize * pointerRatio, firstPoint.Y - this.ArrowSize), new PointF(firstPoint.X + arrowLength - this.ArrowSize * pointerRatio, firstPoint.Y - this.ArrowSize * pointerRatio), new PointF(firstPoint.X + arrowLength, firstPoint.Y), new PointF(firstPoint.X + arrowLength - this.ArrowSize * pointerRatio, firstPoint.Y + this.ArrowSize * pointerRatio), new PointF(firstPoint.X + arrowLength - this.ArrowSize * pointerRatio, firstPoint.Y + this.ArrowSize), new PointF(firstPoint.X + this.ArrowSize * pointerRatio, firstPoint.Y + this.ArrowSize), new PointF(firstPoint.X + this.ArrowSize * pointerRatio, firstPoint.Y + this.ArrowSize * pointerRatio) }; } else if (this.ArrowStyle == ArrowStyle.Tailed) { float tailRatio = 2.1f; points = new PointF[] { firstPoint, new PointF(firstPoint.X + this.ArrowSize * pointerRatio, firstPoint.Y - this.ArrowSize * pointerRatio), new PointF(firstPoint.X + this.ArrowSize * pointerRatio, firstPoint.Y - this.ArrowSize), new PointF(firstPoint.X + arrowLength, firstPoint.Y - this.ArrowSize * tailRatio), new PointF(firstPoint.X + arrowLength - this.ArrowSize * tailRatio, firstPoint.Y), new PointF(firstPoint.X + arrowLength, firstPoint.Y + this.ArrowSize * tailRatio), new PointF(firstPoint.X + this.ArrowSize * pointerRatio, firstPoint.Y + this.ArrowSize), new PointF(firstPoint.X + this.ArrowSize * pointerRatio, firstPoint.Y + this.ArrowSize * pointerRatio) }; } else { throw (new InvalidOperationException(SR.ExceptionAnnotationArrowStyleUnknown)); } path.AddLines(points); path.CloseAllFigures(); // Calculate arrow angle float angle = (float)(Math.Atan(deltaY / deltaX) * 180f / Math.PI); if (deltaX < 0) { angle += 180f; } // Rotate arrow path around the first point using (Matrix matrix = new Matrix()) { matrix.RotateAt(angle, firstPoint); path.Transform(matrix); } return(path); }
/// <summary> /// Paints the annotation object on the specified graphics. /// </summary> /// <param name="graphics"> /// A <see cref="ChartGraphics"/> /// </param> /// <param name="chart"> /// Reference to the <see cref="Chart"/> control that owns the annotation. /// </param> override internal void Paint(Chart chart, ChartGraphics graphics) { // Get annotation position in relative coordinates PointF firstPoint = PointF.Empty; PointF anchorPoint = PointF.Empty; SizeF size = SizeF.Empty; GetRelativePosition(out firstPoint, out size, out anchorPoint); PointF secondPoint = new PointF(firstPoint.X + size.Width, firstPoint.Y + size.Height); // Create selection rectangle RectangleF selectionRect = new RectangleF(firstPoint, new SizeF(secondPoint.X - firstPoint.X, secondPoint.Y - firstPoint.Y)); // Get text position RectangleF rectanglePosition = new RectangleF(selectionRect.Location, selectionRect.Size); if (rectanglePosition.Width < 0) { rectanglePosition.X = rectanglePosition.Right; rectanglePosition.Width = -rectanglePosition.Width; } if (rectanglePosition.Height < 0) { rectanglePosition.Y = rectanglePosition.Bottom; rectanglePosition.Height = -rectanglePosition.Height; } // Check if position is valid if (float.IsNaN(rectanglePosition.X) || float.IsNaN(rectanglePosition.Y) || float.IsNaN(rectanglePosition.Right) || float.IsNaN(rectanglePosition.Bottom)) { return; } if (this.Common.ProcessModePaint) { // Do not draw border if size is less that 10 pixels RectangleF absRectanglePosition = graphics.GetAbsoluteRectangle(rectanglePosition); if (absRectanglePosition.Width > 30f && absRectanglePosition.Height > 30f) { // Draw rectangle graphics.Draw3DBorderRel( _borderSkin, rectanglePosition, this.BackColor, this.BackHatchStyle, String.Empty, ChartImageWrapMode.Scaled, Color.Empty, ChartImageAlignmentStyle.Center, this.BackGradientStyle, this.BackSecondaryColor, this.LineColor, this.LineWidth, this.LineDashStyle); } } // Call base class to paint text, selection handles and process hot regions base.Paint(chart, graphics); }
/// <summary> /// Draws text in specified rectangle. /// </summary> /// <param name="graphics">Chart graphics.</param> /// <param name="textPosition">Text position.</param> /// <param name="noSpacingForCenteredText">True if text allowed to be outside of position when centered.</param> /// <param name="getTextPosition">True if position text must be returned by the method.</param> /// <returns>Text actual position if required.</returns> internal RectangleF DrawText(ChartGraphics graphics, RectangleF textPosition, bool noSpacingForCenteredText, bool getTextPosition) { RectangleF textActualPosition = RectangleF.Empty; //*************************************************************** //** Adjust text position uing text spacing //*************************************************************** bool annotationRelative = false; RectangleF textSpacing = GetTextSpacing(out annotationRelative); float spacingScaleX = 1f; float spacingScaleY = 1f; if (annotationRelative) { if (textPosition.Width > 25f) { spacingScaleX = textPosition.Width / 50f; spacingScaleX = Math.Max(1f, spacingScaleX); } if (textPosition.Height > 25f) { spacingScaleY = textPosition.Height / 50f; spacingScaleY = Math.Max(1f, spacingScaleY); } } RectangleF textPositionWithSpacing = new RectangleF(textPosition.Location, textPosition.Size); textPositionWithSpacing.Width -= (textSpacing.Width + textSpacing.X) * spacingScaleX; textPositionWithSpacing.X += textSpacing.X * spacingScaleX; textPositionWithSpacing.Height -= (textSpacing.Height + textSpacing.Y) * spacingScaleY; textPositionWithSpacing.Y += textSpacing.Y * spacingScaleY; //*************************************************************** //** Replace new line characters //*************************************************************** string titleText = this.ReplaceKeywords(this.Text.Replace("\\n", "\n")); //*************************************************************** //** Check if centered text require spacing. //** Use only half of the spacing required. //** Apply only for 1 line of text. //*************************************************************** if (noSpacingForCenteredText && titleText.IndexOf('\n') == -1) { if (this.Alignment == ContentAlignment.MiddleCenter || this.Alignment == ContentAlignment.MiddleLeft || this.Alignment == ContentAlignment.MiddleRight) { textPositionWithSpacing.Y = textPosition.Y; textPositionWithSpacing.Height = textPosition.Height; textPositionWithSpacing.Height -= textSpacing.Height / 2f + textSpacing.Y / 2f; textPositionWithSpacing.Y += textSpacing.Y / 2f; } if (this.Alignment == ContentAlignment.BottomCenter || this.Alignment == ContentAlignment.MiddleCenter || this.Alignment == ContentAlignment.TopCenter) { textPositionWithSpacing.X = textPosition.X; textPositionWithSpacing.Width = textPosition.Width; textPositionWithSpacing.Width -= textSpacing.Width / 2f + textSpacing.X / 2f; textPositionWithSpacing.X += textSpacing.X / 2f; } } // Draw text using (Brush textBrush = new SolidBrush(this.ForeColor)) { using (StringFormat format = new StringFormat(StringFormat.GenericTypographic)) { //*************************************************************** //** Set text format //*************************************************************** format.FormatFlags = format.FormatFlags ^ StringFormatFlags.LineLimit; format.Trimming = StringTrimming.EllipsisCharacter; if (this.Alignment == ContentAlignment.BottomRight || this.Alignment == ContentAlignment.MiddleRight || this.Alignment == ContentAlignment.TopRight) { format.Alignment = StringAlignment.Far; } if (this.Alignment == ContentAlignment.BottomCenter || this.Alignment == ContentAlignment.MiddleCenter || this.Alignment == ContentAlignment.TopCenter) { format.Alignment = StringAlignment.Center; } if (this.Alignment == ContentAlignment.BottomCenter || this.Alignment == ContentAlignment.BottomLeft || this.Alignment == ContentAlignment.BottomRight) { format.LineAlignment = StringAlignment.Far; } if (this.Alignment == ContentAlignment.MiddleCenter || this.Alignment == ContentAlignment.MiddleLeft || this.Alignment == ContentAlignment.MiddleRight) { format.LineAlignment = StringAlignment.Center; } //*************************************************************** //** Set shadow color and offset //*************************************************************** Color textShadowColor = ChartGraphics.GetGradientColor(this.ForeColor, Color.Black, 0.8); int textShadowOffset = 1; TextStyle textStyle = this.TextStyle; if (textStyle == TextStyle.Shadow && ShadowOffset != 0) { // Draw shadowed text textShadowColor = ShadowColor; textShadowOffset = ShadowOffset; } if (textStyle == TextStyle.Shadow) { textShadowColor = (textShadowColor.A != 255) ? textShadowColor : Color.FromArgb(textShadowColor.A / 2, textShadowColor); } //*************************************************************** //** Get text actual position //*************************************************************** if (getTextPosition) { // Measure text size SizeF textSize = graphics.MeasureStringRel( this.ReplaceKeywords(_text.Replace("\\n", "\n")), this.Font, textPositionWithSpacing.Size, format); // Get text position textActualPosition = new RectangleF(textPositionWithSpacing.Location, textSize); if (this.Alignment == ContentAlignment.BottomRight || this.Alignment == ContentAlignment.MiddleRight || this.Alignment == ContentAlignment.TopRight) { textActualPosition.X += textPositionWithSpacing.Width - textSize.Width; } if (this.Alignment == ContentAlignment.BottomCenter || this.Alignment == ContentAlignment.MiddleCenter || this.Alignment == ContentAlignment.TopCenter) { textActualPosition.X += (textPositionWithSpacing.Width - textSize.Width) / 2f; } if (this.Alignment == ContentAlignment.BottomCenter || this.Alignment == ContentAlignment.BottomLeft || this.Alignment == ContentAlignment.BottomRight) { textActualPosition.Y += textPositionWithSpacing.Height - textSize.Height; } if (this.Alignment == ContentAlignment.MiddleCenter || this.Alignment == ContentAlignment.MiddleLeft || this.Alignment == ContentAlignment.MiddleRight) { textActualPosition.Y += (textPositionWithSpacing.Height - textSize.Height) / 2f; } // Do not allow text to go outside annotation position textActualPosition.Intersect(textPositionWithSpacing); } RectangleF absPosition = graphics.GetAbsoluteRectangle(textPositionWithSpacing); Title.DrawStringWithStyle( graphics, titleText, this.TextStyle, this.Font, absPosition, this.ForeColor, textShadowColor, textShadowOffset, format, TextOrientation.Auto ); } } return(textActualPosition); }