You've already forked linux-packaging-mono
Rewrite with hard-coded offsets into the PE file format to discern if a binary is PE32 or PE32+, and then to determine if it contains a "CLR Data Directory" entry that looks valid. Tested with PE32 and PE32+ compiled Mono binaries, PE32 and PE32+ native binaries, and a random assortment of garbage files. Former-commit-id: 9e7ac86ec84f653a2f79b87183efd5b0ebda001b
5780 lines
208 KiB
C#
5780 lines
208 KiB
C#
//-------------------------------------------------------------
|
||
// <copyright company=’Microsoft Corporation’>
|
||
// Copyright © Microsoft Corporation. All Rights Reserved.
|
||
// </copyright>
|
||
//-------------------------------------------------------------
|
||
// @owner=alexgor, deliant
|
||
//=================================================================
|
||
// File: ChartGraphics.cs
|
||
//
|
||
// Namespace: System.Web.UI.WebControls[Windows.Forms].Charting
|
||
//
|
||
// Classes: ChartGraphics
|
||
//
|
||
// Purpose: Chart graphic class is used for drawing Chart
|
||
// elements as Rectangles, Pie slices, lines, areas
|
||
// etc. This class is used in all classes where
|
||
// drawing is necessary. The GDI+ graphic class is
|
||
// used throw this class. Encapsulates a GDI+ chart
|
||
// drawing functionality
|
||
//
|
||
// Reviewed: GS - Jul 31, 2002
|
||
// AG - August 7, 2002
|
||
// AG - Microsoft 16, 2007
|
||
//
|
||
//===================================================================
|
||
|
||
#region Used namespaces
|
||
|
||
using System;
|
||
using System.Drawing;
|
||
using System.Drawing.Drawing2D;
|
||
using System.Drawing.Text;
|
||
using System.Drawing.Imaging;
|
||
using System.Diagnostics.CodeAnalysis;
|
||
|
||
#if Microsoft_CONTROL
|
||
using System.Windows.Forms.DataVisualization.Charting.Utilities;
|
||
using System.Windows.Forms.DataVisualization.Charting.Borders3D;
|
||
#else
|
||
using System.Web.UI.DataVisualization.Charting.Utilities;
|
||
using System.Web.UI.DataVisualization.Charting.Borders3D;
|
||
#endif
|
||
|
||
#endregion
|
||
|
||
#if Microsoft_CONTROL
|
||
namespace System.Windows.Forms.DataVisualization.Charting
|
||
#else
|
||
namespace System.Web.UI.DataVisualization.Charting
|
||
|
||
#endif
|
||
{
|
||
#region Enumerations
|
||
|
||
/// <summary>
|
||
/// Defines the style how the bars/columns are drawn.
|
||
/// </summary>
|
||
internal enum BarDrawingStyle
|
||
{
|
||
/// <summary>
|
||
/// Default bar/column style.
|
||
/// </summary>
|
||
Default,
|
||
|
||
/// <summary>
|
||
/// Cylinder bar/column style.
|
||
/// </summary>
|
||
Cylinder,
|
||
|
||
/// <summary>
|
||
/// Emboss bar/column style.
|
||
/// </summary>
|
||
Emboss,
|
||
|
||
/// <summary>
|
||
/// LightToDark bar/column style.
|
||
/// </summary>
|
||
LightToDark,
|
||
|
||
/// <summary>
|
||
/// Wedge bar/column style.
|
||
/// </summary>
|
||
Wedge,
|
||
}
|
||
|
||
/// <summary>
|
||
/// Defines the style how the pie and doughnut charts are drawn.
|
||
/// </summary>
|
||
internal enum PieDrawingStyle
|
||
{
|
||
/// <summary>
|
||
/// Default pie/doughnut drawing style.
|
||
/// </summary>
|
||
Default,
|
||
|
||
/// <summary>
|
||
/// Soft edge shadow is drawn on the edges of the pie/doughnut slices.
|
||
/// </summary>
|
||
SoftEdge,
|
||
|
||
/// <summary>
|
||
/// A shadow is drawn from the top to the bottom of the pie/doughnut chart.
|
||
/// </summary>
|
||
Concave,
|
||
}
|
||
|
||
/// <summary>
|
||
/// An enumeration of line styles.
|
||
/// </summary>
|
||
public enum ChartDashStyle
|
||
{
|
||
/// <summary>
|
||
/// Line style not set
|
||
/// </summary>
|
||
NotSet,
|
||
/// <summary>
|
||
/// Specifies a line consisting of dashes.
|
||
/// </summary>
|
||
Dash,
|
||
/// <summary>
|
||
/// Specifies a line consisting of a repeating pattern of dash-dot.
|
||
/// </summary>
|
||
DashDot,
|
||
/// <summary>
|
||
/// Specifies a line consisting of a repeating pattern of dash-dot-dot.
|
||
/// </summary>
|
||
DashDotDot,
|
||
/// <summary>
|
||
/// Specifies a line consisting of dots.
|
||
/// </summary>
|
||
Dot,
|
||
/// <summary>
|
||
/// Specifies a solid line.
|
||
/// </summary>
|
||
Solid,
|
||
}
|
||
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// The ChartGraphics class provides all chart drawing capabilities.
|
||
/// It contains methods for drawing 2D primitives and also exposes
|
||
/// all ChartGraphics3D class methods for 3D shapes. Only this
|
||
/// class should be used for any drawing in the chart.
|
||
/// </summary>
|
||
#if ASPPERM_35
|
||
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
|
||
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
|
||
#endif
|
||
public partial class ChartGraphics : ChartElement
|
||
{
|
||
#region Fields
|
||
|
||
// Common Elements
|
||
private CommonElements _common;
|
||
|
||
// Reusable objects
|
||
private Pen _pen;
|
||
private SolidBrush _solidBrush;
|
||
private Matrix _myMatrix;
|
||
|
||
// Private fields which represents picture size
|
||
private int _width;
|
||
private int _height;
|
||
|
||
// Indicates that smoothing is applied while drawing shadows
|
||
internal bool softShadows = true;
|
||
|
||
// Anti aliasing flags
|
||
private AntiAliasingStyles _antiAliasing = AntiAliasingStyles.All;
|
||
|
||
// True if rendering into the metafile
|
||
internal bool IsMetafile = false;
|
||
|
||
#endregion
|
||
|
||
#region Lines Methods
|
||
|
||
/// <summary>
|
||
/// Draws a line connecting the two specified points.
|
||
/// </summary>
|
||
/// <param name="color">Line color.</param>
|
||
/// <param name="width">Line width.</param>
|
||
/// <param name="style">Line style.</param>
|
||
/// <param name="firstPointF">A Point that represents the first point to connect.</param>
|
||
/// <param name="secondPointF">A Point that represents the second point to connect.</param>
|
||
internal void DrawLineRel(
|
||
Color color,
|
||
int width,
|
||
ChartDashStyle style,
|
||
PointF firstPointF,
|
||
PointF secondPointF
|
||
)
|
||
{
|
||
DrawLineAbs(
|
||
color,
|
||
width,
|
||
style,
|
||
GetAbsolutePoint(firstPointF),
|
||
GetAbsolutePoint(secondPointF) );
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draws a line connecting the two specified points using absolute coordinates.
|
||
/// </summary>
|
||
/// <param name="color">Line color.</param>
|
||
/// <param name="width">Line width.</param>
|
||
/// <param name="style">Line style.</param>
|
||
/// <param name="firstPoint">A Point that represents the first point to connect.</param>
|
||
/// <param name="secondPoint">A Point that represents the second point to connect.</param>
|
||
internal void DrawLineAbs(
|
||
Color color,
|
||
int width,
|
||
ChartDashStyle style,
|
||
PointF firstPoint,
|
||
PointF secondPoint
|
||
)
|
||
{
|
||
// Do not draw line if width is 0 or style not set
|
||
if( width == 0 || style == ChartDashStyle.NotSet )
|
||
{
|
||
return;
|
||
}
|
||
|
||
// Set a line color
|
||
if(_pen.Color != color)
|
||
{
|
||
_pen.Color = color;
|
||
}
|
||
|
||
// Set a line width
|
||
if(_pen.Width != width)
|
||
{
|
||
_pen.Width = width;
|
||
}
|
||
|
||
// Set a line style
|
||
if(_pen.DashStyle != GetPenStyle( style ))
|
||
{
|
||
_pen.DashStyle = GetPenStyle( style );
|
||
}
|
||
|
||
// Remember SmoothingMode and turn off anti aliasing for
|
||
// vertical or horizontal lines usinig 1 pixel dashed pen.
|
||
// This prevents anialiasing from completly smoothing the
|
||
// dashed line.
|
||
SmoothingMode oldSmoothingMode = this.SmoothingMode;
|
||
if(width <= 1 && style != ChartDashStyle.Solid)
|
||
{
|
||
if(firstPoint.X == secondPoint.X ||
|
||
firstPoint.Y == secondPoint.Y)
|
||
{
|
||
this.SmoothingMode = SmoothingMode.Default;
|
||
}
|
||
}
|
||
|
||
// Draw a line
|
||
this.DrawLine(_pen,
|
||
(float)Math.Round(firstPoint.X),
|
||
(float)Math.Round(firstPoint.Y),
|
||
(float)Math.Round(secondPoint.X),
|
||
(float)Math.Round(secondPoint.Y) );
|
||
|
||
// Return old smoothing mode
|
||
this.SmoothingMode = oldSmoothingMode;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draws a line with shadow connecting the two specified points.
|
||
/// </summary>
|
||
/// <param name="color">Line color.</param>
|
||
/// <param name="width">Line width.</param>
|
||
/// <param name="style">Line style.</param>
|
||
/// <param name="firstPoint">A Point that represents the first point to connect.</param>
|
||
/// <param name="secondPoint">A Point that represents the second point to connect.</param>
|
||
/// <param name="shadowColor">Shadow Color.</param>
|
||
/// <param name="shadowOffset">Shadow Offset.</param>
|
||
internal void DrawLineRel(
|
||
Color color,
|
||
int width,
|
||
ChartDashStyle style,
|
||
PointF firstPoint,
|
||
PointF secondPoint,
|
||
Color shadowColor,
|
||
int shadowOffset
|
||
)
|
||
{
|
||
DrawLineAbs(
|
||
color,
|
||
width,
|
||
style,
|
||
GetAbsolutePoint(firstPoint),
|
||
GetAbsolutePoint(secondPoint),
|
||
shadowColor,
|
||
shadowOffset );
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draws a line with shadow connecting the two specified points.
|
||
/// </summary>
|
||
/// <param name="color">Line color.</param>
|
||
/// <param name="width">Line width.</param>
|
||
/// <param name="style">Line style.</param>
|
||
/// <param name="firstPoint">A Point that represents the first point to connect.</param>
|
||
/// <param name="secondPoint">A Point that represents the second point to connect.</param>
|
||
/// <param name="shadowColor">Shadow Color.</param>
|
||
/// <param name="shadowOffset">Shadow Offset.</param>
|
||
internal void DrawLineAbs(
|
||
Color color,
|
||
int width,
|
||
ChartDashStyle style,
|
||
PointF firstPoint,
|
||
PointF secondPoint,
|
||
Color shadowColor,
|
||
int shadowOffset
|
||
)
|
||
{
|
||
if(shadowOffset != 0)
|
||
{
|
||
// Shadow color
|
||
Color shColor;
|
||
|
||
// Make shadow semi transparent
|
||
// if alpha value not used
|
||
if( shadowColor.A != 255 )
|
||
shColor = shadowColor;
|
||
else
|
||
shColor = Color.FromArgb(color.A/2, shadowColor);
|
||
|
||
// Set shadow line position
|
||
PointF firstShadow = new PointF( firstPoint.X + shadowOffset, firstPoint.Y + shadowOffset);
|
||
PointF secondShadow = new PointF( secondPoint.X + shadowOffset, secondPoint.Y + shadowOffset );
|
||
|
||
// Draw Shadow of Line
|
||
DrawLineAbs( shColor, width, style, firstShadow, secondShadow );
|
||
}
|
||
|
||
// Draw Line
|
||
DrawLineAbs( color, width, style, firstPoint, secondPoint );
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Pen and Brush Methods
|
||
|
||
/// <summary>
|
||
/// Creates a Hatch Brush.
|
||
/// </summary>
|
||
/// <param name="hatchStyle">Chart Hatch style.</param>
|
||
/// <param name="backColor">Back Color.</param>
|
||
/// <param name="foreColor">Fore Color.</param>
|
||
/// <returns>Brush</returns>
|
||
internal Brush GetHatchBrush(
|
||
ChartHatchStyle hatchStyle,
|
||
Color backColor,
|
||
Color foreColor
|
||
)
|
||
{
|
||
// Convert Chart Hatch Style enum
|
||
// to Hatch Style enum.
|
||
HatchStyle hatch;
|
||
hatch = (HatchStyle)Enum.Parse(typeof(HatchStyle),hatchStyle.ToString());
|
||
|
||
// Create Hatch Brush
|
||
return new HatchBrush( hatch, foreColor, backColor );
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates a textured brush.
|
||
/// </summary>
|
||
/// <param name="name">Image file name or URL.</param>
|
||
/// <param name="backImageTransparentColor">Image transparent color.</param>
|
||
/// <param name="mode">Wrap mode.</param>
|
||
/// <param name="backColor">Image background color.</param>
|
||
/// <returns>Textured brush.</returns>
|
||
internal Brush GetTextureBrush(
|
||
string name,
|
||
Color backImageTransparentColor,
|
||
ChartImageWrapMode mode,
|
||
Color backColor
|
||
)
|
||
{
|
||
// Load a image
|
||
System.Drawing.Image image = _common.ImageLoader.LoadImage( name );
|
||
|
||
// Create a brush
|
||
ImageAttributes attrib = new ImageAttributes();
|
||
attrib.SetWrapMode((mode == ChartImageWrapMode.Unscaled) ? WrapMode.Clamp : ((WrapMode)mode));
|
||
if(backImageTransparentColor != Color.Empty)
|
||
{
|
||
attrib.SetColorKey(backImageTransparentColor, backImageTransparentColor, ColorAdjustType.Default);
|
||
}
|
||
|
||
// If image is a metafile background must be filled first
|
||
// Solves issue that background is not cleared correctly
|
||
if(backImageTransparentColor == Color.Empty &&
|
||
image is Metafile &&
|
||
backColor != Color.Transparent)
|
||
{
|
||
TextureBrush backFilledBrush = null;
|
||
Bitmap bitmap = new Bitmap(image.Width, image.Height);
|
||
using(Graphics graphics = Graphics.FromImage(bitmap))
|
||
{
|
||
using(SolidBrush backBrush = new SolidBrush(backColor))
|
||
{
|
||
graphics.FillRectangle(backBrush, 0, 0, image.Width, image.Height);
|
||
graphics.DrawImageUnscaled(image, 0, 0);
|
||
backFilledBrush= new TextureBrush( bitmap, new RectangleF(0,0,image.Width,image.Height), attrib);
|
||
}
|
||
}
|
||
|
||
return backFilledBrush;
|
||
}
|
||
|
||
|
||
TextureBrush textureBrush;
|
||
|
||
if (ImageLoader.DoDpisMatch(image, this.Graphics))
|
||
textureBrush = new TextureBrush(image, new RectangleF(0, 0, image.Width, image.Height), attrib);
|
||
else // if the image dpi does not match the graphics dpi we have to scale the image
|
||
{
|
||
Image scaledImage = ImageLoader.GetScaledImage(image, this.Graphics);
|
||
textureBrush = new TextureBrush(scaledImage, new RectangleF(0, 0, scaledImage.Width, scaledImage.Height), attrib);
|
||
scaledImage.Dispose();
|
||
}
|
||
|
||
return textureBrush;
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// This method creates a gradient brush.
|
||
/// </summary>
|
||
/// <param name="rectangle">A rectangle which has to be filled with a gradient color.</param>
|
||
/// <param name="firstColor">First color.</param>
|
||
/// <param name="secondColor">Second color.</param>
|
||
/// <param name="type ">Gradient type .</param>
|
||
/// <returns>Gradient Brush</returns>
|
||
internal Brush GetGradientBrush(
|
||
RectangleF rectangle,
|
||
Color firstColor,
|
||
Color secondColor,
|
||
GradientStyle type
|
||
)
|
||
{
|
||
// Increse the brush rectangle by 1 pixel to ensure the fit
|
||
rectangle.Inflate(1f, 1f);
|
||
|
||
Brush gradientBrush = null;
|
||
float angle = 0;
|
||
|
||
// Function which create gradient brush fires exception if
|
||
// rectangle size is zero.
|
||
if( rectangle.Height == 0 || rectangle.Width == 0 )
|
||
{
|
||
gradientBrush = new SolidBrush( Color.Black );
|
||
return gradientBrush;
|
||
}
|
||
|
||
// *******************************************
|
||
// Linear Gradient
|
||
// *******************************************
|
||
// Check linear type .
|
||
if( type == GradientStyle.LeftRight || type == GradientStyle.VerticalCenter )
|
||
{
|
||
angle = 0;
|
||
}
|
||
else if( type == GradientStyle.TopBottom || type == GradientStyle.HorizontalCenter )
|
||
{
|
||
angle = 90;
|
||
}
|
||
else if( type == GradientStyle.DiagonalLeft )
|
||
{
|
||
angle = (float)(Math.Atan(rectangle.Width / rectangle.Height)* 180 / Math.PI);
|
||
}
|
||
else if( type == GradientStyle.DiagonalRight )
|
||
{
|
||
angle = (float)(180 - Math.Atan(rectangle.Width / rectangle.Height)* 180 / Math.PI);
|
||
}
|
||
|
||
// Create a linear gradient brush
|
||
if( type == GradientStyle.TopBottom || type == GradientStyle.LeftRight
|
||
|| type == GradientStyle.DiagonalLeft || type == GradientStyle.DiagonalRight
|
||
|| type == GradientStyle.HorizontalCenter || type == GradientStyle.VerticalCenter )
|
||
{
|
||
RectangleF tempRect = new RectangleF(rectangle.X,rectangle.Y,rectangle.Width,rectangle.Height);
|
||
// For Horizontal and vertical center gradient types
|
||
if( type == GradientStyle.HorizontalCenter )
|
||
{
|
||
// Resize and wrap gradient
|
||
tempRect.Height = tempRect.Height / 2F;
|
||
LinearGradientBrush linearGradientBrush = new LinearGradientBrush(tempRect, firstColor, secondColor, angle);
|
||
gradientBrush = linearGradientBrush;
|
||
linearGradientBrush.WrapMode = WrapMode.TileFlipX;
|
||
}
|
||
else if( type == GradientStyle.VerticalCenter )
|
||
{
|
||
// Resize and wrap gradient
|
||
tempRect.Width = tempRect.Width / 2F;
|
||
LinearGradientBrush linearGradientBrush = new LinearGradientBrush(tempRect, firstColor, secondColor, angle);
|
||
gradientBrush = linearGradientBrush;
|
||
linearGradientBrush.WrapMode = WrapMode.TileFlipX;
|
||
}
|
||
else
|
||
{
|
||
gradientBrush = new LinearGradientBrush( rectangle, firstColor, secondColor, angle );
|
||
}
|
||
return gradientBrush;
|
||
}
|
||
|
||
// *******************************************
|
||
// Gradient is not linear : From Center.
|
||
// *******************************************
|
||
|
||
// Create a path
|
||
GraphicsPath path = new GraphicsPath();
|
||
|
||
// Add a rectangle to the path
|
||
path.AddRectangle( rectangle );
|
||
|
||
// Create a gradient brush
|
||
PathGradientBrush pathGradientBrush = new PathGradientBrush(path);
|
||
gradientBrush = pathGradientBrush;
|
||
|
||
// Set the center color
|
||
pathGradientBrush.CenterColor = firstColor;
|
||
|
||
// Set the Surround color
|
||
Color[] colors = {secondColor};
|
||
pathGradientBrush.SurroundColors = colors;
|
||
|
||
if( path != null )
|
||
{
|
||
path.Dispose();
|
||
}
|
||
|
||
return gradientBrush;
|
||
}
|
||
|
||
/// <summary>
|
||
/// This method creates a gradient brush for pie. This gradient is one
|
||
/// of the types used only with pie and doughnut.
|
||
/// </summary>
|
||
/// <param name="rectangle">A rectangle which has to be filled with a gradient color</param>
|
||
/// <param name="firstColor">First color</param>
|
||
/// <param name="secondColor">Second color</param>
|
||
/// <returns>Gradient Brush</returns>
|
||
internal Brush GetPieGradientBrush(
|
||
RectangleF rectangle,
|
||
Color firstColor,
|
||
Color secondColor
|
||
)
|
||
{
|
||
// Create a path that consists of a single ellipse.
|
||
GraphicsPath path = new GraphicsPath();
|
||
path.AddEllipse( rectangle );
|
||
|
||
// Use the path to construct a brush.
|
||
PathGradientBrush gradientBrush = new PathGradientBrush(path);
|
||
|
||
// Set the color at the center of the path.
|
||
gradientBrush.CenterColor = firstColor;
|
||
|
||
// Set the color along the entire boundary
|
||
// of the path to aqua.
|
||
Color[] colors = {secondColor};
|
||
|
||
gradientBrush.SurroundColors = colors;
|
||
|
||
if( path != null )
|
||
{
|
||
path.Dispose();
|
||
}
|
||
|
||
return gradientBrush;
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// Converts GDI+ line style to Chart Graph line style.
|
||
/// </summary>
|
||
/// <param name="style">Chart Line style.</param>
|
||
/// <returns>GDI+ line style.</returns>
|
||
internal DashStyle GetPenStyle( ChartDashStyle style )
|
||
{
|
||
// Convert to chart line styles. The custom style doesn’t exist.
|
||
switch( style )
|
||
{
|
||
case ChartDashStyle.Dash:
|
||
return DashStyle.Dash;
|
||
case ChartDashStyle.DashDot:
|
||
return DashStyle.DashDot;
|
||
case ChartDashStyle.DashDotDot:
|
||
return DashStyle.DashDotDot;
|
||
case ChartDashStyle.Dot:
|
||
return DashStyle.Dot;
|
||
}
|
||
|
||
return DashStyle.Solid;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Markers
|
||
|
||
/// <summary>
|
||
/// Creates polygon for multi-corner star marker.
|
||
/// </summary>
|
||
/// <param name="rect">Marker rectangle.</param>
|
||
/// <param name="numberOfCorners">Number of corners (4 and up).</param>
|
||
/// <returns>Array of points.</returns>
|
||
internal PointF[] CreateStarPolygon(RectangleF rect, int numberOfCorners)
|
||
{
|
||
int numberOfCornersX2;
|
||
checked
|
||
{
|
||
numberOfCornersX2 = numberOfCorners * 2;
|
||
}
|
||
|
||
bool outside = true;
|
||
PointF[] points = new PointF[numberOfCornersX2];
|
||
PointF[] tempPoints = new PointF[1];
|
||
// overflow check
|
||
for (int pointIndex = 0; pointIndex < numberOfCornersX2; pointIndex++)
|
||
{
|
||
tempPoints[0] = new PointF(rect.X + rect.Width/2f, (outside == true) ? rect.Y : rect.Y + rect.Height/4f);
|
||
Matrix matrix = new Matrix();
|
||
matrix.RotateAt(pointIndex*(360f/(numberOfCorners*2f)), new PointF(rect.X + rect.Width/2f, rect.Y + rect.Height/2f));
|
||
matrix.TransformPoints(tempPoints);
|
||
points[pointIndex] = tempPoints[0];
|
||
outside = !outside;
|
||
}
|
||
|
||
return points;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draw marker using relative coordinates of the center.
|
||
/// </summary>
|
||
/// <param name="point">Coordinates of the center.</param>
|
||
/// <param name="markerStyle">Marker style.</param>
|
||
/// <param name="markerSize">Marker size.</param>
|
||
/// <param name="markerColor">Marker color.</param>
|
||
/// <param name="markerBorderColor">Marker border color.</param>
|
||
/// <param name="markerBorderSize">Marker border size.</param>
|
||
/// <param name="markerImage">Marker image name.</param>
|
||
/// <param name="markerImageTransparentColor">Marker image transparent color.</param>
|
||
/// <param name="shadowSize">Marker shadow size.</param>
|
||
/// <param name="shadowColor">Marker shadow color.</param>
|
||
/// <param name="imageScaleRect">Rectangle to which marker image should be scaled.</param>
|
||
internal void DrawMarkerRel(
|
||
PointF point,
|
||
MarkerStyle markerStyle,
|
||
int markerSize,
|
||
Color markerColor,
|
||
Color markerBorderColor,
|
||
int markerBorderSize,
|
||
string markerImage,
|
||
Color markerImageTransparentColor,
|
||
int shadowSize,
|
||
Color shadowColor,
|
||
RectangleF imageScaleRect
|
||
)
|
||
{
|
||
DrawMarkerAbs(this.GetAbsolutePoint(point), markerStyle, markerSize, markerColor, markerBorderColor, markerBorderSize, markerImage, markerImageTransparentColor, shadowSize, shadowColor, imageScaleRect, false);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draw marker using absolute coordinates of the center.
|
||
/// </summary>
|
||
/// <param name="point">Coordinates of the center.</param>
|
||
/// <param name="markerStyle">Marker style.</param>
|
||
/// <param name="markerSize">Marker size.</param>
|
||
/// <param name="markerColor">Marker color.</param>
|
||
/// <param name="markerBorderColor">Marker border color.</param>
|
||
/// <param name="markerBorderSize">Marker border size.</param>
|
||
/// <param name="markerImage">Marker image name.</param>
|
||
/// <param name="markerImageTransparentColor">Marker image transparent color.</param>
|
||
/// <param name="shadowSize">Marker shadow size.</param>
|
||
/// <param name="shadowColor">Marker shadow color.</param>
|
||
/// <param name="imageScaleRect">Rectangle to which marker image should be scaled.</param>
|
||
/// <param name="forceAntiAlias">Always use anti aliasing when drawing the marker.</param>
|
||
internal void DrawMarkerAbs(
|
||
PointF point,
|
||
MarkerStyle markerStyle,
|
||
int markerSize,
|
||
Color markerColor,
|
||
Color markerBorderColor,
|
||
int markerBorderSize,
|
||
string markerImage,
|
||
Color markerImageTransparentColor,
|
||
int shadowSize,
|
||
Color shadowColor,
|
||
RectangleF imageScaleRect,
|
||
bool forceAntiAlias
|
||
)
|
||
{
|
||
// Hide border when zero width specified
|
||
if(markerBorderSize <= 0)
|
||
{
|
||
markerBorderColor = Color.Transparent;
|
||
}
|
||
|
||
// Draw image instead of standart markers
|
||
if(markerImage.Length > 0)
|
||
{
|
||
// Get image
|
||
System.Drawing.Image image = _common.ImageLoader.LoadImage( markerImage );
|
||
|
||
if (image != null)
|
||
{
|
||
// Calculate image rectangle
|
||
RectangleF rect = RectangleF.Empty;
|
||
if (imageScaleRect == RectangleF.Empty)
|
||
{
|
||
SizeF size = new SizeF();
|
||
ImageLoader.GetAdjustedImageSize(image, this.Graphics, ref size);
|
||
imageScaleRect.Width = size.Width;
|
||
imageScaleRect.Height = size.Height;
|
||
}
|
||
|
||
rect.X = point.X - imageScaleRect.Width / 2F;
|
||
rect.Y = point.Y - imageScaleRect.Height / 2F;
|
||
rect.Width = imageScaleRect.Width;
|
||
rect.Height = imageScaleRect.Height;
|
||
|
||
// Prepare image properties (transparent color)
|
||
ImageAttributes attrib = new ImageAttributes();
|
||
if (markerImageTransparentColor != Color.Empty)
|
||
{
|
||
attrib.SetColorKey(markerImageTransparentColor, markerImageTransparentColor, ColorAdjustType.Default);
|
||
}
|
||
|
||
// Draw image shadow
|
||
if (shadowSize != 0 && shadowColor != Color.Empty)
|
||
{
|
||
ImageAttributes attribShadow = new ImageAttributes();
|
||
attribShadow.SetColorKey(markerImageTransparentColor, markerImageTransparentColor, ColorAdjustType.Default);
|
||
ColorMatrix colorMatrix = new ColorMatrix();
|
||
colorMatrix.Matrix00 = 0.25f; // Red
|
||
colorMatrix.Matrix11 = 0.25f; // Green
|
||
colorMatrix.Matrix22 = 0.25f; // Blue
|
||
colorMatrix.Matrix33 = 0.5f; // alpha
|
||
colorMatrix.Matrix44 = 1.0f; // w
|
||
attribShadow.SetColorMatrix(colorMatrix);
|
||
|
||
this.DrawImage(image,
|
||
new Rectangle((int)rect.X + shadowSize, (int)rect.Y + shadowSize, (int)rect.Width, (int)rect.Height),
|
||
0, 0, image.Width, image.Height,
|
||
GraphicsUnit.Pixel,
|
||
attribShadow);
|
||
}
|
||
|
||
// Draw image
|
||
this.DrawImage(image,
|
||
new Rectangle((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height),
|
||
0, 0, image.Width, image.Height,
|
||
GraphicsUnit.Pixel,
|
||
attrib);
|
||
}
|
||
}
|
||
|
||
// Draw standart marker using style, size and color
|
||
else if(markerStyle != MarkerStyle.None && markerSize > 0 && markerColor != Color.Empty)
|
||
{
|
||
// Enable antialising
|
||
SmoothingMode oldSmoothingMode = this.SmoothingMode;
|
||
if(forceAntiAlias)
|
||
{
|
||
this.SmoothingMode = SmoothingMode.AntiAlias;
|
||
}
|
||
|
||
// Create solid color brush
|
||
using (SolidBrush brush = new SolidBrush(markerColor))
|
||
{
|
||
// Calculate marker rectangle
|
||
RectangleF rect = RectangleF.Empty;
|
||
rect.X = point.X - ((float)markerSize) / 2F;
|
||
rect.Y = point.Y - ((float)markerSize) / 2F;
|
||
rect.Width = markerSize;
|
||
rect.Height = markerSize;
|
||
|
||
// Draw marker depending on style
|
||
switch (markerStyle)
|
||
{
|
||
case (MarkerStyle.Star4):
|
||
case (MarkerStyle.Star5):
|
||
case (MarkerStyle.Star6):
|
||
case (MarkerStyle.Star10):
|
||
{
|
||
// Set number of corners
|
||
int cornerNumber = 4;
|
||
if (markerStyle == MarkerStyle.Star5)
|
||
{
|
||
cornerNumber = 5;
|
||
}
|
||
else if (markerStyle == MarkerStyle.Star6)
|
||
{
|
||
cornerNumber = 6;
|
||
}
|
||
else if (markerStyle == MarkerStyle.Star10)
|
||
{
|
||
cornerNumber = 10;
|
||
}
|
||
|
||
// Get star polygon
|
||
PointF[] points = CreateStarPolygon(rect, cornerNumber);
|
||
|
||
// Draw shadow
|
||
if (shadowSize != 0 && shadowColor != Color.Empty)
|
||
{
|
||
Matrix translateMatrix = this.Transform.Clone();
|
||
translateMatrix.Translate(shadowSize, shadowSize);
|
||
Matrix oldMatrix = this.Transform;
|
||
this.Transform = translateMatrix;
|
||
|
||
this.FillPolygon(new SolidBrush((shadowColor.A != 255) ? shadowColor : Color.FromArgb(markerColor.A / 2, shadowColor)), points);
|
||
|
||
this.Transform = oldMatrix;
|
||
}
|
||
|
||
// Draw star
|
||
this.FillPolygon(brush, points);
|
||
this.DrawPolygon(new Pen(markerBorderColor, markerBorderSize), points);
|
||
break;
|
||
}
|
||
case (MarkerStyle.Circle):
|
||
{
|
||
// Draw marker shadow
|
||
if (shadowSize != 0 && shadowColor != Color.Empty)
|
||
{
|
||
if (!softShadows)
|
||
{
|
||
using (SolidBrush shadowBrush = new SolidBrush((shadowColor.A != 255) ? shadowColor : Color.FromArgb(markerColor.A / 2, shadowColor)))
|
||
{
|
||
RectangleF shadowRect = rect;
|
||
shadowRect.X += shadowSize;
|
||
shadowRect.Y += shadowSize;
|
||
this.FillEllipse(shadowBrush, shadowRect);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Add circle to the graphics path
|
||
using (GraphicsPath path = new GraphicsPath())
|
||
{
|
||
path.AddEllipse(rect.X + shadowSize - 1, rect.Y + shadowSize - 1, rect.Width + 2, rect.Height + 2);
|
||
|
||
// Create path brush
|
||
using (PathGradientBrush shadowBrush = new PathGradientBrush(path))
|
||
{
|
||
shadowBrush.CenterColor = shadowColor;
|
||
|
||
// Set the color along the entire boundary of the path
|
||
Color[] colors = { Color.Transparent };
|
||
shadowBrush.SurroundColors = colors;
|
||
shadowBrush.CenterPoint = new PointF(point.X, point.Y);
|
||
|
||
// Define brush focus scale
|
||
PointF focusScale = new PointF(1 - 2f * shadowSize / rect.Width, 1 - 2f * shadowSize / rect.Height);
|
||
if (focusScale.X < 0)
|
||
{
|
||
focusScale.X = 0;
|
||
}
|
||
if (focusScale.Y < 0)
|
||
{
|
||
focusScale.Y = 0;
|
||
}
|
||
shadowBrush.FocusScales = focusScale;
|
||
|
||
// Draw shadow
|
||
this.FillPath(shadowBrush, path);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
this.FillEllipse(brush, rect);
|
||
this.DrawEllipse(new Pen(markerBorderColor, markerBorderSize), rect);
|
||
break;
|
||
}
|
||
case (MarkerStyle.Square):
|
||
{
|
||
// Draw marker shadow
|
||
if (shadowSize != 0 && shadowColor != Color.Empty)
|
||
{
|
||
FillRectangleShadowAbs(rect, shadowColor, shadowSize, shadowColor);
|
||
}
|
||
|
||
this.FillRectangle(brush, rect);
|
||
this.DrawRectangle(new Pen(markerBorderColor, markerBorderSize), (int)Math.Round(rect.X, 0), (int)Math.Round(rect.Y, 0), (int)Math.Round(rect.Width, 0), (int)Math.Round(rect.Height, 0));
|
||
break;
|
||
}
|
||
case (MarkerStyle.Cross):
|
||
{
|
||
// Calculate cross line width and size
|
||
float crossLineWidth = (float)Math.Ceiling(markerSize / 4F);
|
||
float crossSize = markerSize;// * (float)Math.Sin(45f/180f*Math.PI);
|
||
|
||
// Calculate cross coordinates
|
||
PointF[] points = new PointF[12];
|
||
points[0].X = point.X - crossSize / 2F;
|
||
points[0].Y = point.Y + crossLineWidth / 2F;
|
||
points[1].X = point.X - crossSize / 2F;
|
||
points[1].Y = point.Y - crossLineWidth / 2F;
|
||
|
||
points[2].X = point.X - crossLineWidth / 2F;
|
||
points[2].Y = point.Y - crossLineWidth / 2F;
|
||
points[3].X = point.X - crossLineWidth / 2F;
|
||
points[3].Y = point.Y - crossSize / 2F;
|
||
points[4].X = point.X + crossLineWidth / 2F;
|
||
points[4].Y = point.Y - crossSize / 2F;
|
||
|
||
points[5].X = point.X + crossLineWidth / 2F;
|
||
points[5].Y = point.Y - crossLineWidth / 2F;
|
||
points[6].X = point.X + crossSize / 2F;
|
||
points[6].Y = point.Y - crossLineWidth / 2F;
|
||
points[7].X = point.X + crossSize / 2F;
|
||
points[7].Y = point.Y + crossLineWidth / 2F;
|
||
|
||
points[8].X = point.X + crossLineWidth / 2F;
|
||
points[8].Y = point.Y + crossLineWidth / 2F;
|
||
points[9].X = point.X + crossLineWidth / 2F;
|
||
points[9].Y = point.Y + crossSize / 2F;
|
||
points[10].X = point.X - crossLineWidth / 2F;
|
||
points[10].Y = point.Y + crossSize / 2F;
|
||
points[11].X = point.X - crossLineWidth / 2F;
|
||
points[11].Y = point.Y + crossLineWidth / 2F;
|
||
|
||
// Rotate cross coordinates 45 degrees
|
||
Matrix rotationMatrix = new Matrix();
|
||
rotationMatrix.RotateAt(45, point);
|
||
rotationMatrix.TransformPoints(points);
|
||
|
||
// Draw shadow
|
||
if (shadowSize != 0 && shadowColor != Color.Empty)
|
||
{
|
||
// Create translation matrix
|
||
Matrix translateMatrix = this.Transform.Clone();
|
||
translateMatrix.Translate(
|
||
(softShadows) ? shadowSize + 1 : shadowSize,
|
||
(softShadows) ? shadowSize + 1 : shadowSize);
|
||
Matrix oldMatrix = this.Transform;
|
||
this.Transform = translateMatrix;
|
||
|
||
if (!softShadows)
|
||
{
|
||
using (Brush softShadowBrush = new SolidBrush((shadowColor.A != 255) ? shadowColor : Color.FromArgb(markerColor.A / 2, shadowColor)))
|
||
{
|
||
this.FillPolygon(softShadowBrush, points);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Add polygon to the graphics path
|
||
using (GraphicsPath path = new GraphicsPath())
|
||
{
|
||
path.AddPolygon(points);
|
||
|
||
// Create path brush
|
||
using (PathGradientBrush shadowBrush = new PathGradientBrush(path))
|
||
{
|
||
shadowBrush.CenterColor = shadowColor;
|
||
|
||
// Set the color along the entire boundary of the path
|
||
Color[] colors = { Color.Transparent };
|
||
shadowBrush.SurroundColors = colors;
|
||
shadowBrush.CenterPoint = new PointF(point.X, point.Y);
|
||
|
||
// Define brush focus scale
|
||
PointF focusScale = new PointF(1 - 2f * shadowSize / rect.Width, 1 - 2f * shadowSize / rect.Height);
|
||
if (focusScale.X < 0)
|
||
{
|
||
focusScale.X = 0;
|
||
}
|
||
if (focusScale.Y < 0)
|
||
{
|
||
focusScale.Y = 0;
|
||
}
|
||
shadowBrush.FocusScales = focusScale;
|
||
|
||
// Draw shadow
|
||
this.FillPath(shadowBrush, path);
|
||
}
|
||
}
|
||
}
|
||
|
||
this.Transform = oldMatrix;
|
||
}
|
||
|
||
// Create translation matrix
|
||
Matrix translateMatrixShape = this.Transform.Clone();
|
||
Matrix oldMatrixShape = this.Transform;
|
||
this.Transform = translateMatrixShape;
|
||
|
||
this.FillPolygon(brush, points);
|
||
this.DrawPolygon(new Pen(markerBorderColor, markerBorderSize), points);
|
||
|
||
this.Transform = oldMatrixShape;
|
||
|
||
break;
|
||
}
|
||
case (MarkerStyle.Diamond):
|
||
{
|
||
PointF[] points = new PointF[4];
|
||
points[0].X = rect.X;
|
||
points[0].Y = rect.Y + rect.Height / 2F;
|
||
points[1].X = rect.X + rect.Width / 2F;
|
||
points[1].Y = rect.Top;
|
||
points[2].X = rect.Right;
|
||
points[2].Y = rect.Y + rect.Height / 2F;
|
||
points[3].X = rect.X + rect.Width / 2F;
|
||
points[3].Y = rect.Bottom;
|
||
|
||
// Draw shadow
|
||
if (shadowSize != 0 && shadowColor != Color.Empty)
|
||
{
|
||
Matrix translateMatrix = this.Transform.Clone();
|
||
translateMatrix.Translate((softShadows) ? 0 : shadowSize,
|
||
(softShadows) ? 0 : shadowSize);
|
||
Matrix oldMatrix = this.Transform;
|
||
this.Transform = translateMatrix;
|
||
|
||
if (!softShadows)
|
||
{
|
||
using (Brush softShadowBrush = new SolidBrush((shadowColor.A != 255) ? shadowColor : Color.FromArgb(markerColor.A / 2, shadowColor)))
|
||
{
|
||
this.FillPolygon(softShadowBrush, points);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Calculate diamond size
|
||
float diamondSize = markerSize * (float)Math.Sin(45f / 180f * Math.PI);
|
||
|
||
// Calculate diamond rectangle position
|
||
RectangleF diamondRect = RectangleF.Empty;
|
||
diamondRect.X = point.X - ((float)diamondSize) / 2F;
|
||
diamondRect.Y = point.Y - ((float)diamondSize) / 2F - shadowSize;
|
||
diamondRect.Width = diamondSize;
|
||
diamondRect.Height = diamondSize;
|
||
|
||
// Set rotation matrix to 45
|
||
translateMatrix.RotateAt(45, point);
|
||
this.Transform = translateMatrix;
|
||
|
||
FillRectangleShadowAbs(diamondRect, shadowColor, shadowSize, shadowColor);
|
||
}
|
||
|
||
|
||
this.Transform = oldMatrix;
|
||
}
|
||
|
||
this.FillPolygon(brush, points);
|
||
this.DrawPolygon(new Pen(markerBorderColor, markerBorderSize), points);
|
||
break;
|
||
}
|
||
case (MarkerStyle.Triangle):
|
||
{
|
||
PointF[] points = new PointF[3];
|
||
points[0].X = rect.X;
|
||
points[0].Y = rect.Bottom;
|
||
points[1].X = rect.X + rect.Width / 2F;
|
||
points[1].Y = rect.Top;
|
||
points[2].X = rect.Right;
|
||
points[2].Y = rect.Bottom;
|
||
|
||
// Draw image shadow
|
||
if (shadowSize != 0 && shadowColor != Color.Empty)
|
||
{
|
||
Matrix translateMatrix = this.Transform.Clone();
|
||
translateMatrix.Translate((softShadows) ? shadowSize - 1 : shadowSize,
|
||
(softShadows) ? shadowSize + 1 : shadowSize);
|
||
Matrix oldMatrix = this.Transform;
|
||
this.Transform = translateMatrix;
|
||
|
||
if (!softShadows)
|
||
{
|
||
using (Brush softShadowBrush = new SolidBrush((shadowColor.A != 255) ? shadowColor : Color.FromArgb(markerColor.A / 2, shadowColor)))
|
||
{
|
||
this.FillPolygon(softShadowBrush, points);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Add polygon to the graphics path
|
||
GraphicsPath path = new GraphicsPath();
|
||
path.AddPolygon(points);
|
||
|
||
// Create path brush
|
||
PathGradientBrush shadowBrush = new PathGradientBrush(path);
|
||
shadowBrush.CenterColor = shadowColor;
|
||
|
||
// Set the color along the entire boundary of the path
|
||
Color[] colors = { Color.Transparent };
|
||
shadowBrush.SurroundColors = colors;
|
||
shadowBrush.CenterPoint = new PointF(point.X, point.Y);
|
||
|
||
// Define brush focus scale
|
||
PointF focusScale = new PointF(1 - 2f * shadowSize / rect.Width, 1 - 2f * shadowSize / rect.Height);
|
||
if (focusScale.X < 0)
|
||
{
|
||
focusScale.X = 0;
|
||
}
|
||
if (focusScale.Y < 0)
|
||
{
|
||
focusScale.Y = 0;
|
||
}
|
||
shadowBrush.FocusScales = focusScale;
|
||
|
||
// Draw shadow
|
||
this.FillPath(shadowBrush, path);
|
||
}
|
||
|
||
this.Transform = oldMatrix;
|
||
}
|
||
|
||
this.FillPolygon(brush, points);
|
||
this.DrawPolygon(new Pen(markerBorderColor, markerBorderSize), points);
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
throw (new InvalidOperationException(SR.ExceptionGraphicsMarkerStyleUnknown));
|
||
}
|
||
}
|
||
}
|
||
|
||
// Restore SmoothingMode
|
||
if(forceAntiAlias)
|
||
{
|
||
this.SmoothingMode = oldSmoothingMode;
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region String Methods
|
||
|
||
/// <summary>
|
||
/// Measures the specified string when drawn with the specified
|
||
/// Font object and formatted with the specified StringFormat object.
|
||
/// </summary>
|
||
/// <param name="text">String to measure.</param>
|
||
/// <param name="font">Font object defines the text format of the string.</param>
|
||
/// <param name="layoutArea">SizeF structure that specifies the maximum layout area for the text.</param>
|
||
/// <param name="stringFormat">StringFormat object that represents formatting information, such as line spacing, for the string.</param>
|
||
/// <param name="textOrientation">Text orientation.</param>
|
||
/// <returns>This method returns a SizeF structure that represents the size, in pixels, of the string specified in the text parameter as drawn with the font parameter and the stringFormat parameter.</returns>
|
||
internal SizeF MeasureString(
|
||
string text,
|
||
Font font,
|
||
SizeF layoutArea,
|
||
StringFormat stringFormat,
|
||
TextOrientation textOrientation
|
||
)
|
||
{
|
||
// Current implementation of the stacked text will simply insert a new
|
||
// line character between all characters in the original string. This
|
||
// apporach will not allow to show multiple lines of stacked text or
|
||
// correctly handle text wrapping.
|
||
if (textOrientation == TextOrientation.Stacked)
|
||
{
|
||
text = GetStackedText(text);
|
||
}
|
||
return this.MeasureString(text, font, layoutArea, stringFormat);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Measures the specified text string when drawn with
|
||
/// the specified Font object and formatted with the
|
||
/// specified StringFormat object.
|
||
/// </summary>
|
||
/// <param name="text">The string to measure</param>
|
||
/// <param name="font">The Font object used to determine the size of the text string. </param>
|
||
/// <param name="layoutArea">A SizeF structure that specifies the layout rectangle for the text. </param>
|
||
/// <param name="stringFormat">A StringFormat object that represents formatting information, such as line spacing, for the text string. </param>
|
||
/// <param name="textOrientation">Text orientation.</param>
|
||
/// <returns>A SizeF structure that represents the size of text as drawn with font.</returns>
|
||
internal SizeF MeasureStringRel(
|
||
string text,
|
||
Font font,
|
||
SizeF layoutArea,
|
||
StringFormat stringFormat,
|
||
TextOrientation textOrientation)
|
||
{
|
||
// Current implementation of the stacked text will simply insert a new
|
||
// line character between all characters in the original string. This
|
||
// apporach will not allow to show multiple lines of stacked text or
|
||
// correctly handle text wrapping.
|
||
if (textOrientation == TextOrientation.Stacked)
|
||
{
|
||
text = GetStackedText(text);
|
||
}
|
||
return this.MeasureStringRel(text, font, layoutArea, stringFormat);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draws the specified text string at the specified location with the specified Brush and Font objects using the formatting properties of the specified StringFormat object.
|
||
/// </summary>
|
||
/// <param name="text">String to draw.</param>
|
||
/// <param name="font">Font object that defines the text format of the string.</param>
|
||
/// <param name="brush">Brush object that determines the color and texture of the drawn text.</param>
|
||
/// <param name="rect">Position of the drawn text in pixels.</param>
|
||
/// <param name="format">StringFormat object that specifies formatting properties, such as line spacing and alignment, that are applied to the drawn text.</param>
|
||
/// <param name="textOrientation">Text orientation.</param>
|
||
internal void DrawString(
|
||
string text,
|
||
Font font,
|
||
Brush brush,
|
||
RectangleF rect,
|
||
StringFormat format,
|
||
TextOrientation textOrientation
|
||
)
|
||
{
|
||
// Current implementation of the stacked text will simply insert a new
|
||
// line character between all characters in the original string. This
|
||
// apporach will not allow to show multiple lines of stacked text or
|
||
// correctly handle text wrapping.
|
||
if (textOrientation == TextOrientation.Stacked)
|
||
{
|
||
text = GetStackedText(text);
|
||
}
|
||
this.DrawString(text, font, brush, rect, format);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draw a string.
|
||
/// </summary>
|
||
/// <param name="text">Text.</param>
|
||
/// <param name="font">Text Font.</param>
|
||
/// <param name="brush">Text Brush.</param>
|
||
/// <param name="position">Text Position.</param>
|
||
/// <param name="format">Format and text alignment.</param>
|
||
/// <param name="angle">Text angle.</param>
|
||
/// <param name="textOrientation">Text orientation.</param>
|
||
internal void DrawStringRel(
|
||
string text,
|
||
System.Drawing.Font font,
|
||
System.Drawing.Brush brush,
|
||
PointF position,
|
||
System.Drawing.StringFormat format,
|
||
int angle,
|
||
TextOrientation textOrientation
|
||
)
|
||
{
|
||
// Current implementation of the stacked text will simply insert a new
|
||
// line character between all characters in the original string. This
|
||
// apporach will not allow to show multiple lines of stacked text or
|
||
// correctly handle text wrapping.
|
||
if (textOrientation == TextOrientation.Stacked)
|
||
{
|
||
text = GetStackedText(text);
|
||
}
|
||
|
||
this.DrawStringRel(text, font, brush, position, format, angle);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draw a string.
|
||
/// </summary>
|
||
/// <param name="text">Text.</param>
|
||
/// <param name="font">Text Font.</param>
|
||
/// <param name="brush">Text Brush.</param>
|
||
/// <param name="position">Text Position.</param>
|
||
/// <param name="format">Format and text alignment.</param>
|
||
/// <param name="textOrientation">Text orientation.</param>
|
||
internal void DrawStringRel(
|
||
string text,
|
||
System.Drawing.Font font,
|
||
System.Drawing.Brush brush,
|
||
RectangleF position,
|
||
System.Drawing.StringFormat format,
|
||
TextOrientation textOrientation
|
||
)
|
||
{
|
||
// Current implementation of the stacked text will simply insert a new
|
||
// line character between all characters in the original string. This
|
||
// apporach will not allow to show multiple lines of stacked text or
|
||
// correctly handle text wrapping.
|
||
if (textOrientation == TextOrientation.Stacked)
|
||
{
|
||
text = GetStackedText(text);
|
||
}
|
||
|
||
this.DrawStringRel(text, font, brush, position, format);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Function returned stacked text by inserting new line characters between
|
||
/// all characters in the original string.
|
||
/// </summary>
|
||
/// <param name="text">Original text.</param>
|
||
/// <returns>Stacked text.</returns>
|
||
internal static string GetStackedText(string text)
|
||
{
|
||
string result = string.Empty;
|
||
foreach (char ch in text)
|
||
{
|
||
result += ch;
|
||
if (ch != '\n')
|
||
{
|
||
result += '\n';
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draw a string and fills it's background
|
||
/// </summary>
|
||
/// <param name="common">The Common elements object.</param>
|
||
/// <param name="text">Text.</param>
|
||
/// <param name="font">Text Font.</param>
|
||
/// <param name="brush">Text Brush.</param>
|
||
/// <param name="position">Text Position.</param>
|
||
/// <param name="format">Format and text alignment.</param>
|
||
/// <param name="angle">Text angle.</param>
|
||
/// <param name="backPosition">Text background position.</param>
|
||
/// <param name="backColor">Back Color</param>
|
||
/// <param name="borderColor">Border Color</param>
|
||
/// <param name="borderWidth">Border Width</param>
|
||
/// <param name="borderDashStyle">Border Style</param>
|
||
/// <param name="series">Series</param>
|
||
/// <param name="point">Point</param>
|
||
/// <param name="pointIndex">Point index in series</param>
|
||
internal void DrawPointLabelStringRel(
|
||
CommonElements common,
|
||
string text,
|
||
System.Drawing.Font font,
|
||
System.Drawing.Brush brush,
|
||
RectangleF position,
|
||
System.Drawing.StringFormat format,
|
||
int angle,
|
||
RectangleF backPosition,
|
||
Color backColor,
|
||
Color borderColor,
|
||
int borderWidth,
|
||
ChartDashStyle borderDashStyle,
|
||
Series series,
|
||
DataPoint point,
|
||
int pointIndex)
|
||
{
|
||
// Start Svg/Flash Selection mode
|
||
this.StartHotRegion( point, true );
|
||
|
||
// Draw background
|
||
DrawPointLabelBackground(
|
||
common,
|
||
angle,
|
||
PointF.Empty,
|
||
backPosition,
|
||
backColor,
|
||
borderColor,
|
||
borderWidth,
|
||
borderDashStyle,
|
||
series,
|
||
point,
|
||
pointIndex);
|
||
|
||
// End Svg/Flash Selection mode
|
||
this.EndHotRegion( );
|
||
|
||
point._lastLabelText = text;
|
||
// Draw text
|
||
if (IsRightToLeft)
|
||
{
|
||
// datapoint label alignments should appear as not RTL.
|
||
using (StringFormat fmt = (StringFormat)format.Clone())
|
||
{
|
||
if (fmt.Alignment == StringAlignment.Far)
|
||
{
|
||
fmt.Alignment = StringAlignment.Near;
|
||
}
|
||
else if (fmt.Alignment == StringAlignment.Near)
|
||
{
|
||
fmt.Alignment = StringAlignment.Far;
|
||
}
|
||
DrawStringRel(text,font,brush,position,fmt,angle);
|
||
}
|
||
}
|
||
else
|
||
DrawStringRel(text, font, brush, position, format, angle);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draw a string and fills it's background
|
||
/// </summary>
|
||
/// <param name="common">The Common elements object.</param>
|
||
/// <param name="text">Text.</param>
|
||
/// <param name="font">Text Font.</param>
|
||
/// <param name="brush">Text Brush.</param>
|
||
/// <param name="position">Text Position.</param>
|
||
/// <param name="format">Format and text alignment.</param>
|
||
/// <param name="angle">Text angle.</param>
|
||
/// <param name="backPosition">Text background position.</param>
|
||
/// <param name="backColor">Back Color</param>
|
||
/// <param name="borderColor">Border Color</param>
|
||
/// <param name="borderWidth">Border Width</param>
|
||
/// <param name="borderDashStyle">Border Style</param>
|
||
/// <param name="series">Series</param>
|
||
/// <param name="point">Point</param>
|
||
/// <param name="pointIndex">Point index in series</param>
|
||
internal void DrawPointLabelStringRel(
|
||
CommonElements common,
|
||
string text,
|
||
System.Drawing.Font font,
|
||
System.Drawing.Brush brush,
|
||
PointF position,
|
||
System.Drawing.StringFormat format,
|
||
int angle,
|
||
RectangleF backPosition,
|
||
Color backColor,
|
||
Color borderColor,
|
||
int borderWidth,
|
||
ChartDashStyle borderDashStyle,
|
||
Series series,
|
||
DataPoint point,
|
||
int pointIndex)
|
||
{
|
||
// Start Svg/Flash Selection mode
|
||
this.StartHotRegion( point, true );
|
||
|
||
// Draw background
|
||
DrawPointLabelBackground(
|
||
common,
|
||
angle,
|
||
position,
|
||
backPosition,
|
||
backColor,
|
||
borderColor,
|
||
borderWidth,
|
||
borderDashStyle,
|
||
series,
|
||
point,
|
||
pointIndex);
|
||
|
||
// End Svg/Flash Selection mode
|
||
this.EndHotRegion( );
|
||
|
||
point._lastLabelText = text;
|
||
// Draw text
|
||
if (IsRightToLeft)
|
||
{
|
||
// datapoint label alignments should appear as not RTL
|
||
using (StringFormat fmt = (StringFormat)format.Clone())
|
||
{
|
||
if (fmt.Alignment == StringAlignment.Far)
|
||
{
|
||
fmt.Alignment = StringAlignment.Near;
|
||
}
|
||
else if (fmt.Alignment == StringAlignment.Near)
|
||
{
|
||
fmt.Alignment = StringAlignment.Far;
|
||
}
|
||
DrawStringRel(text,font,brush,position,fmt,angle);
|
||
}
|
||
}
|
||
else
|
||
DrawStringRel(text,font,brush,position,format,angle);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draw a string and fills it's background
|
||
/// </summary>
|
||
/// <param name="common">The Common elements object.</param>
|
||
/// <param name="angle">Text angle.</param>
|
||
/// <param name="textPosition">Text position.</param>
|
||
/// <param name="backPosition">Text background position.</param>
|
||
/// <param name="backColor">Back Color</param>
|
||
/// <param name="borderColor">Border Color</param>
|
||
/// <param name="borderWidth">Border Width</param>
|
||
/// <param name="borderDashStyle">Border Style</param>
|
||
/// <param name="series">Series</param>
|
||
/// <param name="point">Point</param>
|
||
/// <param name="pointIndex">Point index in series</param>
|
||
private void DrawPointLabelBackground(
|
||
CommonElements common,
|
||
int angle,
|
||
PointF textPosition,
|
||
RectangleF backPosition,
|
||
Color backColor,
|
||
Color borderColor,
|
||
int borderWidth,
|
||
ChartDashStyle borderDashStyle,
|
||
Series series,
|
||
DataPoint point,
|
||
int pointIndex)
|
||
{
|
||
// Draw background
|
||
if(!backPosition.IsEmpty)
|
||
{
|
||
RectangleF backPositionAbs = this.Round(this.GetAbsoluteRectangle(backPosition));
|
||
|
||
// Get rotation point
|
||
PointF rotationPoint = PointF.Empty;
|
||
if(textPosition.IsEmpty)
|
||
{
|
||
rotationPoint = new PointF(backPositionAbs.X + backPositionAbs.Width/2f, backPositionAbs.Y + backPositionAbs.Height/2f);
|
||
}
|
||
else
|
||
{
|
||
rotationPoint = this.GetAbsolutePoint(textPosition);
|
||
}
|
||
|
||
// Create a matrix and rotate it.
|
||
_myMatrix = this.Transform.Clone();
|
||
_myMatrix.RotateAt( angle, rotationPoint );
|
||
|
||
// Save old state
|
||
GraphicsState graphicsState = this.Save();
|
||
|
||
// Set transformatino
|
||
this.Transform = _myMatrix;
|
||
|
||
// Check for empty colors
|
||
if( !backColor.IsEmpty ||
|
||
!borderColor.IsEmpty)
|
||
{
|
||
// Fill box around the label
|
||
using(Brush brush = new SolidBrush(backColor))
|
||
{
|
||
this.FillRectangle(brush, backPositionAbs);
|
||
}
|
||
|
||
// deliant: Fix VSTS #156433 (2) Data Label Border in core always shows when the style is set to NotSet
|
||
// Draw box border
|
||
if( borderWidth > 0 &&
|
||
!borderColor.IsEmpty && borderDashStyle != ChartDashStyle.NotSet)
|
||
{
|
||
AntiAliasingStyles saveAntiAliasing = this.AntiAliasing;
|
||
try
|
||
{
|
||
this.AntiAliasing = AntiAliasingStyles.None;
|
||
using(Pen pen = new Pen(borderColor, borderWidth))
|
||
{
|
||
pen.DashStyle = GetPenStyle( borderDashStyle );
|
||
this.DrawRectangle(
|
||
pen,
|
||
backPositionAbs.X,
|
||
backPositionAbs.Y,
|
||
backPositionAbs.Width,
|
||
backPositionAbs.Height);
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
this.AntiAliasing = saveAntiAliasing;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Draw invisible rectangle to handle tooltips
|
||
using(Brush brush = new SolidBrush(Color.Transparent))
|
||
{
|
||
this.FillRectangle(brush, backPositionAbs);
|
||
}
|
||
}
|
||
|
||
|
||
// Restore old state
|
||
this.Restore(graphicsState);
|
||
|
||
// Add point label hot region
|
||
if( common != null &&
|
||
common.ProcessModeRegions)
|
||
{
|
||
#if !Microsoft_CONTROL
|
||
// Remember all point attributes
|
||
string oldToolTip = point.IsCustomPropertySet( CommonCustomProperties.ToolTip) ? point.ToolTip : null;
|
||
string oldUrl = point.IsCustomPropertySet( CommonCustomProperties.Url) ? point.Url : null;
|
||
string oldMapAreaAttributes = point.IsCustomPropertySet( CommonCustomProperties.MapAreaAttributes) ? point.MapAreaAttributes : null;
|
||
string oldPostback = point.IsCustomPropertySet( CommonCustomProperties.PostBackValue) ? point.PostBackValue : null;
|
||
object oldTag = point.Tag;
|
||
// Set label attributes into the point attribute.
|
||
// Workaround for the AddHotRegion method limitation.
|
||
point.ToolTip = point.LabelToolTip;
|
||
point.Url = point.LabelUrl;
|
||
point.MapAreaAttributes = point.LabelMapAreaAttributes;
|
||
point.PostBackValue = point.PostBackValue;
|
||
#endif // !Microsoft_CONTROL
|
||
|
||
// Insert area
|
||
if(angle == 0)
|
||
{
|
||
common.HotRegionsList.AddHotRegion(
|
||
backPosition,
|
||
point,
|
||
series.Name,
|
||
pointIndex );
|
||
}
|
||
else
|
||
{
|
||
// Convert rectangle to the graphics path and apply rotation transformation
|
||
using (GraphicsPath path = new GraphicsPath())
|
||
{
|
||
path.AddRectangle(backPositionAbs);
|
||
path.Transform(_myMatrix);
|
||
|
||
// Add hot region
|
||
common.HotRegionsList.AddHotRegion(
|
||
path,
|
||
false,
|
||
this,
|
||
point,
|
||
series.Name,
|
||
pointIndex);
|
||
}
|
||
}
|
||
|
||
#if !Microsoft_CONTROL
|
||
// Restore all point attributes
|
||
if (oldToolTip != null) point.ToolTip = oldToolTip; else point.ResetToolTip();
|
||
if (oldUrl != null) point.Url = oldUrl; else point.ResetUrl();
|
||
if (oldMapAreaAttributes != null) point.MapAreaAttributes = oldMapAreaAttributes; else point.ResetMapAreaAttributes();
|
||
if (oldPostback != null) point.PostBackValue = oldPostback; else point.ResetPostBackValue();
|
||
point.Tag = oldTag;
|
||
#endif // !Microsoft_CONTROL
|
||
|
||
// Set new hot region element type
|
||
if (common.HotRegionsList.List != null && common.HotRegionsList.List.Count > 0)
|
||
{
|
||
((HotRegion)common.HotRegionsList.List[common.HotRegionsList.List.Count - 1]).Type =
|
||
ChartElementType.DataPointLabel;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draw a string.
|
||
/// </summary>
|
||
/// <param name="text">Text.</param>
|
||
/// <param name="font">Text Font.</param>
|
||
/// <param name="brush">Text Brush.</param>
|
||
/// <param name="position">Text Position.</param>
|
||
/// <param name="format">Format and text alignment.</param>
|
||
/// <param name="angle">Text angle.</param>
|
||
internal void DrawStringRel(
|
||
string text,
|
||
System.Drawing.Font font,
|
||
System.Drawing.Brush brush,
|
||
PointF position,
|
||
System.Drawing.StringFormat format,
|
||
int angle
|
||
)
|
||
{
|
||
DrawStringAbs(
|
||
text,
|
||
font,
|
||
brush,
|
||
GetAbsolutePoint(position),
|
||
format,
|
||
angle);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draw a string.
|
||
/// </summary>
|
||
/// <param name="text">Text.</param>
|
||
/// <param name="font">Text Font.</param>
|
||
/// <param name="brush">Text Brush.</param>
|
||
/// <param name="absPosition">Text Position.</param>
|
||
/// <param name="format">Format and text alignment.</param>
|
||
/// <param name="angle">Text angle.</param>
|
||
internal void DrawStringAbs(
|
||
string text,
|
||
System.Drawing.Font font,
|
||
System.Drawing.Brush brush,
|
||
PointF absPosition,
|
||
System.Drawing.StringFormat format,
|
||
int angle
|
||
)
|
||
{
|
||
// Create a matrix and rotate it.
|
||
_myMatrix = this.Transform.Clone();
|
||
_myMatrix.RotateAt(angle, absPosition);
|
||
|
||
// Save aold state
|
||
GraphicsState graphicsState = this.Save();
|
||
|
||
// Set Angle
|
||
this.Transform = _myMatrix;
|
||
|
||
// Draw text with anti-aliasing
|
||
/*
|
||
if( (AntiAliasing & AntiAliasing.Text) == AntiAliasing.Text )
|
||
this.TextRenderingHint = TextRenderingHint.AntiAlias;
|
||
else
|
||
this.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;
|
||
*/
|
||
|
||
// Draw a string
|
||
this.DrawString( text, font, brush, absPosition , format );
|
||
|
||
// Restore old state
|
||
this.Restore(graphicsState);
|
||
}
|
||
|
||
/// <summary>
|
||
/// This method is used by the axis title hot region generation code.
|
||
/// It transforms the centered rectangle the same way as the Axis title text.
|
||
/// </summary>
|
||
/// <param name="center">Title center</param>
|
||
/// <param name="size">Title text size</param>
|
||
/// <param name="angle">Title rotation angle</param>
|
||
/// <returns></returns>
|
||
internal GraphicsPath GetTranformedTextRectPath(PointF center, SizeF size, int angle)
|
||
{
|
||
// Text hot area is 10px greater than the size of text
|
||
size.Width += 10;
|
||
size.Height += 10;
|
||
|
||
// Get the absolute center and create the centered rectangle points
|
||
PointF absCenter = GetAbsolutePoint(center);
|
||
PointF[] points = new PointF[] {
|
||
new PointF(absCenter.X - size.Width / 2f, absCenter.Y - size.Height / 2f),
|
||
new PointF(absCenter.X + size.Width / 2f, absCenter.Y - size.Height / 2f),
|
||
new PointF(absCenter.X + size.Width / 2f, absCenter.Y + size.Height / 2f),
|
||
new PointF(absCenter.X - size.Width / 2f, absCenter.Y + size.Height / 2f)};
|
||
|
||
//Prepare the same tranformation matrix as used for the axis title
|
||
Matrix matrix = this.Transform.Clone();
|
||
matrix.RotateAt(angle, absCenter);
|
||
//Tranform the rectangle points
|
||
matrix.TransformPoints(points);
|
||
|
||
//Return the path consisting of the rect points
|
||
GraphicsPath path = new GraphicsPath();
|
||
path.AddLines(points);
|
||
path.CloseAllFigures();
|
||
return path;
|
||
}
|
||
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// Draw label string.
|
||
/// </summary>
|
||
/// <param name="axis">Label axis.</param>
|
||
/// <param name="labelRowIndex">Label text row index (0-10).</param>
|
||
/// <param name="labelMark">Second row labels mark style.</param>
|
||
/// <param name="markColor">Label mark line color.</param>
|
||
/// <param name="text">Label text.</param>
|
||
/// <param name="image">Label image name.</param>
|
||
/// <param name="imageTransparentColor">Label image transparent color.</param>
|
||
/// <param name="font">Text bont.</param>
|
||
/// <param name="brush">Text brush.</param>
|
||
/// <param name="position">Text position rectangle.</param>
|
||
/// <param name="format">Label text format.</param>
|
||
/// <param name="angle">Label text angle.</param>
|
||
/// <param name="boundaryRect">Specifies the rectangle where the label text MUST be fitted.</param>
|
||
/// <param name="label">Custom Label Item</param>
|
||
/// <param name="truncatedLeft">Label is truncated on the left.</param>
|
||
/// <param name="truncatedRight">Label is truncated on the right.</param>
|
||
internal void DrawLabelStringRel(
|
||
Axis axis,
|
||
int labelRowIndex,
|
||
LabelMarkStyle labelMark,
|
||
Color markColor,
|
||
string text,
|
||
string image,
|
||
Color imageTransparentColor,
|
||
System.Drawing.Font font,
|
||
System.Drawing.Brush brush,
|
||
RectangleF position,
|
||
System.Drawing.StringFormat format,
|
||
int angle,
|
||
RectangleF boundaryRect,
|
||
CustomLabel label,
|
||
bool truncatedLeft,
|
||
bool truncatedRight)
|
||
{
|
||
Matrix oldTransform;
|
||
using (StringFormat drawingFormat = (StringFormat)format.Clone())
|
||
{
|
||
SizeF labelSize = SizeF.Empty;
|
||
|
||
// Check that rectangle is not empty
|
||
if (position.Width == 0 || position.Height == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// Find absolute position
|
||
RectangleF absPosition = this.GetAbsoluteRectangle(position);
|
||
|
||
// Make sure the rectangle is not empty
|
||
if (absPosition.Width < 1f)
|
||
{
|
||
absPosition.Width = 1f;
|
||
}
|
||
if (absPosition.Height < 1f)
|
||
{
|
||
absPosition.Height = 1f;
|
||
}
|
||
|
||
#if DEBUG
|
||
// TESTING CODE: Shows labels rectangle position.
|
||
// Rectangle rr = Rectangle.Round(absPosition);
|
||
// rr.Width = (int)Math.Round(absPosition.Right) - rr.X;
|
||
// rr.Height = (int)Math.Round(absPosition.Bottom) - rr.Y;
|
||
// this.DrawRectangle(Pens.Red,rr.X, rr.Y, rr.Width, rr.Height);
|
||
#endif // DEBUG
|
||
|
||
CommonElements common = axis.Common;
|
||
if (common.ProcessModeRegions)
|
||
{
|
||
common.HotRegionsList.AddHotRegion(Rectangle.Round(absPosition), label, ChartElementType.AxisLabels, false, true);
|
||
}
|
||
|
||
//********************************************************************
|
||
//** Draw labels in the second row
|
||
//********************************************************************
|
||
if (labelRowIndex > 0)
|
||
{
|
||
drawingFormat.LineAlignment = StringAlignment.Center;
|
||
drawingFormat.Alignment = StringAlignment.Center;
|
||
angle = 0;
|
||
|
||
if (axis.AxisPosition == AxisPosition.Left)
|
||
{
|
||
angle = -90;
|
||
}
|
||
else if (axis.AxisPosition == AxisPosition.Right)
|
||
{
|
||
angle = 90;
|
||
}
|
||
else if (axis.AxisPosition == AxisPosition.Top)
|
||
{
|
||
}
|
||
else if (axis.AxisPosition == AxisPosition.Bottom)
|
||
{
|
||
}
|
||
}
|
||
|
||
//********************************************************************
|
||
//** Calculate rotation point
|
||
//********************************************************************
|
||
PointF rotationPoint = PointF.Empty;
|
||
if (axis.AxisPosition == AxisPosition.Left)
|
||
{
|
||
rotationPoint.X = absPosition.Right;
|
||
rotationPoint.Y = absPosition.Y + absPosition.Height / 2F;
|
||
}
|
||
else if (axis.AxisPosition == AxisPosition.Right)
|
||
{
|
||
rotationPoint.X = absPosition.Left;
|
||
rotationPoint.Y = absPosition.Y + absPosition.Height / 2F;
|
||
}
|
||
else if (axis.AxisPosition == AxisPosition.Top)
|
||
{
|
||
rotationPoint.X = absPosition.X + absPosition.Width / 2F;
|
||
rotationPoint.Y = absPosition.Bottom;
|
||
}
|
||
else if (axis.AxisPosition == AxisPosition.Bottom)
|
||
{
|
||
rotationPoint.X = absPosition.X + absPosition.Width / 2F;
|
||
rotationPoint.Y = absPosition.Top;
|
||
}
|
||
|
||
//********************************************************************
|
||
//** Adjust rectangle for horisontal axis
|
||
//********************************************************************
|
||
if ((axis.AxisPosition == AxisPosition.Top || axis.AxisPosition == AxisPosition.Bottom) &&
|
||
angle != 0)
|
||
{
|
||
// Get rectangle center
|
||
rotationPoint.X = absPosition.X + absPosition.Width / 2F;
|
||
rotationPoint.Y = (axis.AxisPosition == AxisPosition.Top) ? absPosition.Bottom : absPosition.Y;
|
||
|
||
// Rotate rectangle 90 degrees
|
||
RectangleF newRect = RectangleF.Empty;
|
||
newRect.X = absPosition.X + absPosition.Width / 2F;
|
||
newRect.Y = absPosition.Y - absPosition.Width / 2F;
|
||
newRect.Height = absPosition.Width;
|
||
newRect.Width = absPosition.Height;
|
||
|
||
// Adjust values for bottom axis
|
||
if (axis.AxisPosition == AxisPosition.Bottom)
|
||
{
|
||
if (angle < 0)
|
||
{
|
||
newRect.X -= newRect.Width;
|
||
}
|
||
|
||
// Replace string alignment
|
||
drawingFormat.Alignment = StringAlignment.Near;
|
||
if (angle < 0)
|
||
{
|
||
drawingFormat.Alignment = StringAlignment.Far;
|
||
}
|
||
drawingFormat.LineAlignment = StringAlignment.Center;
|
||
}
|
||
|
||
// Adjust values for bottom axis
|
||
if (axis.AxisPosition == AxisPosition.Top)
|
||
{
|
||
newRect.Y += absPosition.Height;
|
||
if (angle > 0)
|
||
{
|
||
newRect.X -= newRect.Width;
|
||
}
|
||
|
||
// Replace string alignment
|
||
drawingFormat.Alignment = StringAlignment.Far;
|
||
if (angle < 0)
|
||
{
|
||
drawingFormat.Alignment = StringAlignment.Near;
|
||
}
|
||
drawingFormat.LineAlignment = StringAlignment.Center;
|
||
}
|
||
|
||
// Set new label rect
|
||
absPosition = newRect;
|
||
}
|
||
|
||
//********************************************************************
|
||
//** 90 degrees is a special case for vertical axes
|
||
//********************************************************************
|
||
if ((axis.AxisPosition == AxisPosition.Left || axis.AxisPosition == AxisPosition.Right) &&
|
||
(angle == 90 || angle == -90))
|
||
{
|
||
// Get rectangle center
|
||
rotationPoint.X = absPosition.X + absPosition.Width / 2F;
|
||
rotationPoint.Y = absPosition.Y + absPosition.Height / 2F;
|
||
|
||
// Rotate rectangle 90 degrees
|
||
RectangleF newRect = RectangleF.Empty;
|
||
newRect.X = rotationPoint.X - absPosition.Height / 2F;
|
||
newRect.Y = rotationPoint.Y - absPosition.Width / 2F;
|
||
newRect.Height = absPosition.Width;
|
||
newRect.Width = absPosition.Height;
|
||
absPosition = newRect;
|
||
|
||
// Replace string alignment
|
||
StringAlignment align = drawingFormat.Alignment;
|
||
drawingFormat.Alignment = drawingFormat.LineAlignment;
|
||
drawingFormat.LineAlignment = align;
|
||
if (angle == 90)
|
||
{
|
||
if (drawingFormat.LineAlignment == StringAlignment.Far)
|
||
drawingFormat.LineAlignment = StringAlignment.Near;
|
||
else if (drawingFormat.LineAlignment == StringAlignment.Near)
|
||
drawingFormat.LineAlignment = StringAlignment.Far;
|
||
}
|
||
if (angle == -90)
|
||
{
|
||
if (drawingFormat.Alignment == StringAlignment.Far)
|
||
drawingFormat.Alignment = StringAlignment.Near;
|
||
else if (drawingFormat.Alignment == StringAlignment.Near)
|
||
drawingFormat.Alignment = StringAlignment.Far;
|
||
}
|
||
}
|
||
|
||
//********************************************************************
|
||
//** Create a matrix and rotate it.
|
||
//********************************************************************
|
||
oldTransform = null;
|
||
if (angle != 0)
|
||
{
|
||
_myMatrix = this.Transform.Clone();
|
||
_myMatrix.RotateAt(angle, rotationPoint);
|
||
|
||
// Old angle
|
||
oldTransform = this.Transform;
|
||
|
||
// Set Angle
|
||
this.Transform = _myMatrix;
|
||
}
|
||
|
||
//********************************************************************
|
||
//** Measure string exact rectangle and adjust label bounding rectangle
|
||
//********************************************************************
|
||
RectangleF labelRect = Rectangle.Empty;
|
||
float offsetY = 0f;
|
||
float offsetX = 0f;
|
||
|
||
// Measure text size
|
||
labelSize = this.MeasureString(text.Replace("\\n", "\n"), font, absPosition.Size, drawingFormat);
|
||
|
||
// Calculate text rectangle
|
||
labelRect.Width = labelSize.Width;
|
||
labelRect.Height = labelSize.Height;
|
||
if (drawingFormat.Alignment == StringAlignment.Far)
|
||
{
|
||
labelRect.X = absPosition.Right - labelSize.Width;
|
||
}
|
||
else if (drawingFormat.Alignment == StringAlignment.Near)
|
||
{
|
||
labelRect.X = absPosition.X;
|
||
}
|
||
else if (drawingFormat.Alignment == StringAlignment.Center)
|
||
{
|
||
labelRect.X = absPosition.X + absPosition.Width / 2F - labelSize.Width / 2F;
|
||
}
|
||
|
||
if (drawingFormat.LineAlignment == StringAlignment.Far)
|
||
{
|
||
labelRect.Y = absPosition.Bottom - labelSize.Height;
|
||
}
|
||
else if (drawingFormat.LineAlignment == StringAlignment.Near)
|
||
{
|
||
labelRect.Y = absPosition.Y;
|
||
}
|
||
else if (drawingFormat.LineAlignment == StringAlignment.Center)
|
||
{
|
||
labelRect.Y = absPosition.Y + absPosition.Height / 2F - labelSize.Height / 2F;
|
||
}
|
||
|
||
//If the angle is not vertical or horizontal
|
||
if (angle != 0 && angle != 90 && angle != -90)
|
||
{
|
||
// Adjust label rectangle so it will not overlap the plotting area
|
||
offsetY = (float)Math.Sin((90 - angle) / 180F * Math.PI) * labelRect.Height / 2F;
|
||
offsetX = (float)Math.Sin((Math.Abs(angle)) / 180F * Math.PI) * labelRect.Height / 2F;
|
||
|
||
if (axis.AxisPosition == AxisPosition.Left)
|
||
{
|
||
_myMatrix.Translate(-offsetX, 0);
|
||
}
|
||
else if (axis.AxisPosition == AxisPosition.Right)
|
||
{
|
||
_myMatrix.Translate(offsetX, 0);
|
||
}
|
||
else if (axis.AxisPosition == AxisPosition.Top)
|
||
{
|
||
_myMatrix.Translate(0, -offsetY);
|
||
}
|
||
else if (axis.AxisPosition == AxisPosition.Bottom)
|
||
{
|
||
_myMatrix.Translate(0, offsetY);
|
||
}
|
||
|
||
// Adjust label rectangle so it will be inside boundary
|
||
if (boundaryRect != RectangleF.Empty)
|
||
{
|
||
Region region = new Region(labelRect);
|
||
region.Transform(_myMatrix);
|
||
|
||
// Extend boundary rectangle to the chart picture border
|
||
if (axis.AxisPosition == AxisPosition.Left)
|
||
{
|
||
boundaryRect.Width += boundaryRect.X;
|
||
boundaryRect.X = 0;
|
||
}
|
||
else if (axis.AxisPosition == AxisPosition.Right)
|
||
{
|
||
boundaryRect.Width = this._common.Width - boundaryRect.X;
|
||
}
|
||
else if (axis.AxisPosition == AxisPosition.Top)
|
||
{
|
||
boundaryRect.Height += boundaryRect.Y;
|
||
boundaryRect.Y = 0;
|
||
}
|
||
else if (axis.AxisPosition == AxisPosition.Bottom)
|
||
{
|
||
boundaryRect.Height = this._common.Height - boundaryRect.Y;
|
||
}
|
||
|
||
// Exclude boundary rectangle from the label rectangle
|
||
region.Exclude(this.GetAbsoluteRectangle(boundaryRect));
|
||
|
||
// If any part of the label was outside bounding rectangle
|
||
if (!region.IsEmpty(Graphics))
|
||
{
|
||
this.Transform = oldTransform;
|
||
RectangleF truncateRect = region.GetBounds(Graphics);
|
||
|
||
float sizeChange = truncateRect.Width / (float)Math.Cos(Math.Abs(angle) / 180F * Math.PI);
|
||
if (axis.AxisPosition == AxisPosition.Left)
|
||
{
|
||
sizeChange -= labelRect.Height * (float)Math.Tan(Math.Abs(angle) / 180F * Math.PI);
|
||
absPosition.Y = labelRect.Y;
|
||
absPosition.X = labelRect.X + sizeChange;
|
||
absPosition.Width = labelRect.Width - sizeChange;
|
||
absPosition.Height = labelRect.Height;
|
||
}
|
||
else if (axis.AxisPosition == AxisPosition.Right)
|
||
{
|
||
sizeChange -= labelRect.Height * (float)Math.Tan(Math.Abs(angle) / 180F * Math.PI);
|
||
absPosition.Y = labelRect.Y;
|
||
absPosition.X = labelRect.X;
|
||
absPosition.Width = labelRect.Width - sizeChange;
|
||
absPosition.Height = labelRect.Height;
|
||
}
|
||
else if (axis.AxisPosition == AxisPosition.Top)
|
||
{
|
||
absPosition.Y = labelRect.Y;
|
||
absPosition.X = labelRect.X;
|
||
absPosition.Width = labelRect.Width - sizeChange;
|
||
absPosition.Height = labelRect.Height;
|
||
if (angle > 0)
|
||
{
|
||
absPosition.X += sizeChange;
|
||
}
|
||
}
|
||
else if (axis.AxisPosition == AxisPosition.Bottom)
|
||
{
|
||
absPosition.Y = labelRect.Y;
|
||
absPosition.X = labelRect.X;
|
||
absPosition.Width = labelRect.Width - sizeChange;
|
||
absPosition.Height = labelRect.Height;
|
||
if (angle < 0)
|
||
{
|
||
absPosition.X += sizeChange;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Update transformation matrix
|
||
this.Transform = _myMatrix;
|
||
}
|
||
|
||
//********************************************************************
|
||
//** Reserve space on the left for the label iamge
|
||
//********************************************************************
|
||
RectangleF absPositionWithoutImage = new RectangleF(absPosition.Location, absPosition.Size);
|
||
|
||
System.Drawing.Image labelImage = null;
|
||
SizeF imageAbsSize = new SizeF();
|
||
|
||
if (image.Length > 0)
|
||
{
|
||
labelImage = axis.Common.ImageLoader.LoadImage(label.Image);
|
||
|
||
if (labelImage != null)
|
||
{
|
||
ImageLoader.GetAdjustedImageSize(labelImage, this.Graphics, ref imageAbsSize);
|
||
|
||
// Adjust label position using image size
|
||
absPositionWithoutImage.Width -= imageAbsSize.Width;
|
||
absPositionWithoutImage.X += imageAbsSize.Width;
|
||
}
|
||
|
||
if (absPositionWithoutImage.Width < 1f)
|
||
{
|
||
absPositionWithoutImage.Width = 1f;
|
||
}
|
||
|
||
}
|
||
|
||
//********************************************************************
|
||
//** Draw tick marks for labels in second row
|
||
//********************************************************************
|
||
if (labelRowIndex > 0 && labelMark != LabelMarkStyle.None)
|
||
{
|
||
// Make sure that me know the exact size of the text
|
||
labelSize = this.MeasureString(
|
||
text.Replace("\\n", "\n"),
|
||
font,
|
||
absPositionWithoutImage.Size,
|
||
drawingFormat);
|
||
|
||
// Adjust for label image
|
||
SizeF labelSizeWithImage = new SizeF(labelSize.Width, labelSize.Height);
|
||
if (labelImage != null)
|
||
{
|
||
labelSizeWithImage.Width += imageAbsSize.Width;
|
||
}
|
||
|
||
// Draw mark
|
||
DrawSecondRowLabelMark(
|
||
axis,
|
||
markColor,
|
||
absPosition,
|
||
labelSizeWithImage,
|
||
labelMark,
|
||
truncatedLeft,
|
||
truncatedRight,
|
||
oldTransform);
|
||
}
|
||
|
||
//********************************************************************
|
||
//** Make sure that one line label will not disapear with LineLimit
|
||
//** flag on.
|
||
//********************************************************************
|
||
if ((drawingFormat.FormatFlags & StringFormatFlags.LineLimit) != 0)
|
||
{
|
||
// Measure string height out of one character
|
||
drawingFormat.FormatFlags ^= StringFormatFlags.LineLimit;
|
||
SizeF size = this.MeasureString("I", font, absPosition.Size, drawingFormat);
|
||
|
||
// If height of one characte is more than rectangle heigjt - remove LineLimit flag
|
||
if (size.Height < absPosition.Height)
|
||
{
|
||
drawingFormat.FormatFlags |= StringFormatFlags.LineLimit;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Set NoClip flag
|
||
if ((drawingFormat.FormatFlags & StringFormatFlags.NoClip) != 0)
|
||
{
|
||
drawingFormat.FormatFlags ^= StringFormatFlags.NoClip;
|
||
}
|
||
|
||
// Measure string height out of one character without clipping
|
||
SizeF size = this.MeasureString("I", font, absPosition.Size, drawingFormat);
|
||
|
||
// Clear NoClip flag
|
||
drawingFormat.FormatFlags ^= StringFormatFlags.NoClip;
|
||
|
||
// If height of one characte is more than rectangle heigt - set NoClip flag
|
||
if (size.Height > absPosition.Height)
|
||
{
|
||
float delta = size.Height - absPosition.Height;
|
||
absPosition.Y -= delta / 2f;
|
||
absPosition.Height += delta;
|
||
}
|
||
}
|
||
|
||
//********************************************************************
|
||
//** Draw a string
|
||
//********************************************************************
|
||
if (IsRightToLeft)
|
||
{
|
||
// label alignment on the axis should appear as not RTL.
|
||
using (StringFormat fmt = (StringFormat)drawingFormat.Clone())
|
||
{
|
||
|
||
if (fmt.Alignment == StringAlignment.Far)
|
||
{
|
||
fmt.Alignment = StringAlignment.Near;
|
||
}
|
||
else if (fmt.Alignment == StringAlignment.Near)
|
||
{
|
||
fmt.Alignment = StringAlignment.Far;
|
||
}
|
||
this.DrawString(text.Replace("\\n", "\n"), font, brush,
|
||
absPositionWithoutImage,
|
||
fmt);
|
||
|
||
}
|
||
}
|
||
else
|
||
this.DrawString(text.Replace("\\n", "\n"), font, brush,
|
||
absPositionWithoutImage,
|
||
drawingFormat);
|
||
|
||
// Add separate hot region for the label
|
||
if (common.ProcessModeRegions)
|
||
{
|
||
using (GraphicsPath path = new GraphicsPath())
|
||
{
|
||
path.AddRectangle(labelRect);
|
||
path.Transform(this.Transform);
|
||
string url = string.Empty;
|
||
string mapAreaAttributes = string.Empty;
|
||
string postbackValue = string.Empty;
|
||
#if !Microsoft_CONTROL
|
||
url = label.Url;
|
||
mapAreaAttributes = label.MapAreaAttributes;
|
||
postbackValue = label.PostBackValue;
|
||
#endif // !Microsoft_CONTROL
|
||
common.HotRegionsList.AddHotRegion(
|
||
this,
|
||
path,
|
||
false,
|
||
label.ToolTip,
|
||
url,
|
||
mapAreaAttributes,
|
||
postbackValue,
|
||
label,
|
||
ChartElementType.AxisLabels);
|
||
}
|
||
}
|
||
|
||
//********************************************************************
|
||
//** Draw an image
|
||
//********************************************************************
|
||
if (labelImage != null)
|
||
{
|
||
// Make sure we no the text size
|
||
if (labelSize.IsEmpty)
|
||
{
|
||
labelSize = this.MeasureString(
|
||
text.Replace("\\n", "\n"),
|
||
font,
|
||
absPositionWithoutImage.Size,
|
||
drawingFormat);
|
||
}
|
||
|
||
// Calculate image rectangle
|
||
RectangleF imageRect = new RectangleF(
|
||
absPosition.X + (absPosition.Width - imageAbsSize.Width - labelSize.Width) / 2,
|
||
absPosition.Y + (absPosition.Height - imageAbsSize.Height) / 2,
|
||
imageAbsSize.Width,
|
||
imageAbsSize.Height);
|
||
|
||
if (drawingFormat.LineAlignment == StringAlignment.Center)
|
||
{
|
||
imageRect.Y = absPosition.Y + (absPosition.Height - imageAbsSize.Height) / 2;
|
||
}
|
||
else if (drawingFormat.LineAlignment == StringAlignment.Far)
|
||
{
|
||
imageRect.Y = absPosition.Bottom - (labelSize.Height + imageAbsSize.Height) / 2;
|
||
}
|
||
else if (drawingFormat.LineAlignment == StringAlignment.Near)
|
||
{
|
||
imageRect.Y = absPosition.Top + (labelSize.Height - imageAbsSize.Height) / 2;
|
||
}
|
||
|
||
if (drawingFormat.Alignment == StringAlignment.Center)
|
||
{
|
||
imageRect.X = absPosition.X + (absPosition.Width - imageAbsSize.Width - labelSize.Width) / 2;
|
||
}
|
||
else if (drawingFormat.Alignment == StringAlignment.Far)
|
||
{
|
||
imageRect.X = absPosition.Right - imageAbsSize.Width - labelSize.Width;
|
||
}
|
||
else if (drawingFormat.Alignment == StringAlignment.Near)
|
||
{
|
||
imageRect.X = absPosition.X;
|
||
}
|
||
|
||
// Create image attribute
|
||
ImageAttributes attrib = new ImageAttributes();
|
||
if (imageTransparentColor != Color.Empty)
|
||
{
|
||
attrib.SetColorKey(imageTransparentColor, imageTransparentColor, ColorAdjustType.Default);
|
||
}
|
||
|
||
// Draw image
|
||
this.DrawImage(
|
||
labelImage,
|
||
Rectangle.Round(imageRect),
|
||
0, 0, labelImage.Width, labelImage.Height,
|
||
GraphicsUnit.Pixel,
|
||
attrib);
|
||
|
||
// Add separate hot region for the label image
|
||
if (common.ProcessModeRegions)
|
||
{
|
||
using (GraphicsPath path = new GraphicsPath())
|
||
{
|
||
path.AddRectangle(imageRect);
|
||
path.Transform(this.Transform);
|
||
string imageUrl = string.Empty;
|
||
string imageMapAreaAttributes = string.Empty;
|
||
string postbackValue = string.Empty;
|
||
#if !Microsoft_CONTROL
|
||
imageUrl = label.ImageUrl;
|
||
imageMapAreaAttributes = label.ImageMapAreaAttributes;
|
||
postbackValue = label.PostBackValue;
|
||
#endif // !Microsoft_CONTROL
|
||
common.HotRegionsList.AddHotRegion(
|
||
this,
|
||
path,
|
||
false,
|
||
string.Empty,
|
||
imageUrl,
|
||
imageMapAreaAttributes,
|
||
postbackValue,
|
||
label,
|
||
ChartElementType.AxisLabelImage);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Set Old Angle
|
||
if(oldTransform != null)
|
||
{
|
||
this.Transform = oldTransform;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draw box marks for the labels in second row
|
||
/// </summary>
|
||
/// <param name="axis">Axis object.</param>
|
||
/// <param name="markColor">Label mark color.</param>
|
||
/// <param name="absPosition">Absolute position of the text.</param>
|
||
/// <param name="truncatedLeft">Label is truncated on the left.</param>
|
||
/// <param name="truncatedRight">Label is truncated on the right.</param>
|
||
/// <param name="originalTransform">Original transformation matrix.</param>
|
||
private void DrawSecondRowLabelBoxMark(
|
||
Axis axis,
|
||
Color markColor,
|
||
RectangleF absPosition,
|
||
bool truncatedLeft,
|
||
bool truncatedRight,
|
||
Matrix originalTransform)
|
||
{
|
||
// Remeber current and then reset original matrix
|
||
Matrix curentMatrix = this.Transform;
|
||
if(originalTransform != null)
|
||
{
|
||
this.Transform = originalTransform;
|
||
}
|
||
|
||
// Calculate center of the text rectangle
|
||
PointF centerNotRound = new PointF(absPosition.X + absPosition.Width/2F, absPosition.Y + absPosition.Height/2F);
|
||
|
||
// Rotate rectangle 90 degrees
|
||
if( axis.AxisPosition == AxisPosition.Left || axis.AxisPosition == AxisPosition.Right)
|
||
{
|
||
RectangleF newRect = RectangleF.Empty;
|
||
newRect.X = centerNotRound.X - absPosition.Height / 2F;
|
||
newRect.Y = centerNotRound.Y - absPosition.Width / 2F;
|
||
newRect.Height = absPosition.Width;
|
||
newRect.Width = absPosition.Height;
|
||
absPosition = newRect;
|
||
}
|
||
|
||
// Get axis position
|
||
float axisPosRelative = (float)axis.GetAxisPosition(true);
|
||
PointF axisPositionAbs = new PointF(axisPosRelative, axisPosRelative);
|
||
axisPositionAbs = this.GetAbsolutePoint(axisPositionAbs);
|
||
|
||
// Round position to achieve crisp lines with antialiasing
|
||
Rectangle absPositionRounded = Rectangle.Round(absPosition);
|
||
|
||
// Make sure the right and bottom position is not shifted during rounding
|
||
absPositionRounded.Width = (int)Math.Round(absPosition.Right) - absPositionRounded.X;
|
||
absPositionRounded.Height = (int)Math.Round(absPosition.Bottom) - absPositionRounded.Y;
|
||
|
||
// Create pen
|
||
Pen markPen = new Pen(
|
||
(markColor.IsEmpty) ? axis.MajorTickMark.LineColor : markColor,
|
||
axis.MajorTickMark.LineWidth);
|
||
|
||
// Set pen style
|
||
markPen.DashStyle = GetPenStyle( axis.MajorTickMark.LineDashStyle );
|
||
|
||
// Draw top/bottom lines
|
||
if( axis.AxisPosition == AxisPosition.Left || axis.AxisPosition == AxisPosition.Right)
|
||
{
|
||
this.DrawLine(markPen, absPositionRounded.Left, absPositionRounded.Top, absPositionRounded.Left, absPositionRounded.Bottom);
|
||
this.DrawLine(markPen, absPositionRounded.Right, absPositionRounded.Top, absPositionRounded.Right, absPositionRounded.Bottom);
|
||
}
|
||
else
|
||
{
|
||
this.DrawLine(markPen, absPositionRounded.Left, absPositionRounded.Top, absPositionRounded.Right, absPositionRounded.Top);
|
||
this.DrawLine(markPen, absPositionRounded.Left, absPositionRounded.Bottom, absPositionRounded.Right, absPositionRounded.Bottom);
|
||
}
|
||
|
||
// Draw left line
|
||
if(!truncatedLeft)
|
||
{
|
||
if( axis.AxisPosition == AxisPosition.Left || axis.AxisPosition == AxisPosition.Right)
|
||
{
|
||
this.DrawLine(
|
||
markPen,
|
||
(axis.AxisPosition == AxisPosition.Left) ? absPositionRounded.Left : absPositionRounded.Right,
|
||
absPositionRounded.Bottom,
|
||
axisPositionAbs.X,
|
||
absPositionRounded.Bottom);
|
||
}
|
||
else
|
||
{
|
||
this.DrawLine(
|
||
markPen,
|
||
absPositionRounded.Left,
|
||
(axis.AxisPosition == AxisPosition.Top) ? absPositionRounded.Top : absPositionRounded.Bottom,
|
||
absPositionRounded.Left,
|
||
axisPositionAbs.Y);
|
||
}
|
||
}
|
||
|
||
// Draw right line
|
||
if(!truncatedRight)
|
||
{
|
||
if( axis.AxisPosition == AxisPosition.Left || axis.AxisPosition == AxisPosition.Right)
|
||
{
|
||
this.DrawLine(
|
||
markPen,
|
||
(axis.AxisPosition == AxisPosition.Left) ? absPositionRounded.Left : absPositionRounded.Right,
|
||
absPositionRounded.Top,
|
||
axisPositionAbs.X,
|
||
absPositionRounded.Top);
|
||
}
|
||
else
|
||
{
|
||
this.DrawLine(
|
||
markPen,
|
||
absPositionRounded.Right,
|
||
(axis.AxisPosition == AxisPosition.Top) ? absPositionRounded.Top : absPositionRounded.Bottom,
|
||
absPositionRounded.Right,
|
||
axisPositionAbs.Y);
|
||
}
|
||
}
|
||
|
||
// Dispose Pen
|
||
if( markPen != null )
|
||
{
|
||
markPen.Dispose();
|
||
}
|
||
|
||
// Restore currentmatrix
|
||
if(originalTransform != null)
|
||
{
|
||
this.Transform = curentMatrix;
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// Draw marks for the labels in second row
|
||
/// </summary>
|
||
/// <param name="axis">Axis object.</param>
|
||
/// <param name="markColor">Label mark color.</param>
|
||
/// <param name="absPosition">Absolute position of the text.</param>
|
||
/// <param name="labelSize">Exact mesured size of the text.</param>
|
||
/// <param name="labelMark">Label mark style to draw.</param>
|
||
/// <param name="truncatedLeft">Label is truncated on the left.</param>
|
||
/// <param name="truncatedRight">Label is truncated on the right.</param>
|
||
/// <param name="oldTransform">Original transformation matrix.</param>
|
||
private void DrawSecondRowLabelMark(
|
||
Axis axis,
|
||
Color markColor,
|
||
RectangleF absPosition,
|
||
SizeF labelSize,
|
||
LabelMarkStyle labelMark,
|
||
bool truncatedLeft,
|
||
bool truncatedRight,
|
||
Matrix oldTransform)
|
||
{
|
||
// Do not draw marking line if width is 0 and style or color are not set
|
||
if( axis.MajorTickMark.LineWidth == 0 ||
|
||
axis.MajorTickMark.LineDashStyle == ChartDashStyle.NotSet ||
|
||
axis.MajorTickMark.LineColor == Color.Empty)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// Remember SmoothingMode and turn off anti aliasing for
|
||
// vertical or horizontal lines of the label markers.
|
||
SmoothingMode oldSmoothingMode = this.SmoothingMode;
|
||
this.SmoothingMode = SmoothingMode.None;
|
||
|
||
|
||
// Draw box marker
|
||
if(labelMark == LabelMarkStyle.Box)
|
||
{
|
||
DrawSecondRowLabelBoxMark(
|
||
axis,
|
||
markColor,
|
||
absPosition,
|
||
truncatedLeft,
|
||
truncatedRight,
|
||
oldTransform);
|
||
}
|
||
else
|
||
|
||
{
|
||
// Calculate center of the text rectangle
|
||
Point center = Point.Round(new PointF(absPosition.X + absPosition.Width/2F, absPosition.Y + absPosition.Height/2F));
|
||
|
||
// Round position to achieve crisp lines with antialiasing
|
||
Rectangle absPositionRounded = Rectangle.Round(absPosition);
|
||
|
||
// Make sure the right and bottom position is not shifted during rounding
|
||
absPositionRounded.Width = (int)Math.Round(absPosition.Right) - absPositionRounded.X;
|
||
absPositionRounded.Height = (int)Math.Round(absPosition.Bottom) - absPositionRounded.Y;
|
||
|
||
|
||
// Arrays of points for the left and right marking lines
|
||
PointF[] leftLine = new PointF[3];
|
||
PointF[] rightLine = new PointF[3];
|
||
|
||
// Calculate marking lines coordinates
|
||
leftLine[0].X = absPositionRounded.Left;
|
||
leftLine[0].Y = absPositionRounded.Bottom;
|
||
leftLine[1].X = absPositionRounded.Left;
|
||
leftLine[1].Y = center.Y;
|
||
leftLine[2].X = (float)Math.Round((double)center.X - labelSize.Width/2F - 1F);
|
||
leftLine[2].Y = center.Y;
|
||
|
||
rightLine[0].X = absPositionRounded.Right;
|
||
rightLine[0].Y = absPositionRounded.Bottom;
|
||
rightLine[1].X = absPositionRounded.Right;
|
||
rightLine[1].Y = center.Y;
|
||
rightLine[2].X = (float)Math.Round((double)center.X + labelSize.Width/2F - 1F);
|
||
rightLine[2].Y = center.Y;
|
||
|
||
if(axis.AxisPosition == AxisPosition.Bottom)
|
||
{
|
||
leftLine[0].Y = absPositionRounded.Top;
|
||
rightLine[0].Y = absPositionRounded.Top;
|
||
}
|
||
|
||
// Remove third point to draw only side marks
|
||
if(labelMark == LabelMarkStyle.SideMark)
|
||
{
|
||
leftLine[2] = leftLine[1];
|
||
rightLine[2] = rightLine[1];
|
||
}
|
||
|
||
if(truncatedLeft)
|
||
{
|
||
leftLine[0] = leftLine[1];
|
||
}
|
||
if(truncatedRight)
|
||
{
|
||
rightLine[0] = rightLine[1];
|
||
}
|
||
|
||
// Create pen
|
||
Pen markPen = new Pen(
|
||
(markColor.IsEmpty) ? axis.MajorTickMark.LineColor : markColor,
|
||
axis.MajorTickMark.LineWidth);
|
||
|
||
// Set pen style
|
||
markPen.DashStyle = GetPenStyle( axis.MajorTickMark.LineDashStyle );
|
||
|
||
// Draw marking lines
|
||
this.DrawLines(markPen, leftLine);
|
||
this.DrawLines(markPen, rightLine);
|
||
|
||
// Dispose Pen
|
||
if( markPen != null )
|
||
{
|
||
markPen.Dispose();
|
||
}
|
||
}
|
||
|
||
// Restore previous SmoothingMode
|
||
this.SmoothingMode = oldSmoothingMode;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Measures the specified text string when drawn with
|
||
/// the specified Font object and formatted with the
|
||
/// specified StringFormat object.
|
||
/// </summary>
|
||
/// <param name="text">The string to measure</param>
|
||
/// <param name="font">The Font object used to determine the size of the text string. </param>
|
||
/// <returns>A SizeF structure that represents the size of text as drawn with font.</returns>
|
||
internal SizeF MeasureStringRel( string text, Font font )
|
||
{
|
||
SizeF newSize;
|
||
|
||
// Measure string
|
||
newSize = this.MeasureString( text, font );
|
||
|
||
// Convert to relative Coordinates
|
||
return GetRelativeSize( newSize );
|
||
}
|
||
|
||
/// <summary>
|
||
/// Measures the specified text string when drawn with
|
||
/// the specified Font object and formatted with the
|
||
/// specified StringFormat object.
|
||
/// </summary>
|
||
/// <param name="text">The string to measure</param>
|
||
/// <param name="font">The Font object used to determine the size of the text string. </param>
|
||
/// <param name="layoutArea">A SizeF structure that specifies the layout rectangle for the text. </param>
|
||
/// <param name="stringFormat">A StringFormat object that represents formatting information, such as line spacing, for the text string. </param>
|
||
/// <returns>A SizeF structure that represents the size of text as drawn with font.</returns>
|
||
internal SizeF MeasureStringRel( string text, Font font, SizeF layoutArea, StringFormat stringFormat )
|
||
{
|
||
SizeF size, newSize;
|
||
|
||
// Get absolute coordinates
|
||
size = GetAbsoluteSize( layoutArea );
|
||
|
||
newSize = this.MeasureString( text, font, size, stringFormat );
|
||
|
||
// Convert to relative Coordinates
|
||
return GetRelativeSize( newSize );
|
||
}
|
||
|
||
/// <summary>
|
||
/// Measures the specified text string when drawn with
|
||
/// the specified Font object and formatted with the
|
||
/// specified StringFormat object.
|
||
/// </summary>
|
||
/// <param name="text">The string to measure</param>
|
||
/// <param name="font">The Font object used to determine the size of the text string. </param>
|
||
/// <returns>A SizeF structure that represents the size of text as drawn with font.</returns>
|
||
internal Size MeasureStringAbs( string text, Font font )
|
||
{
|
||
// Measure string
|
||
SizeF size = this.MeasureString( text, font );
|
||
return new Size( (int)Math.Ceiling(size.Width), (int)Math.Ceiling(size.Height));
|
||
}
|
||
|
||
/// <summary>
|
||
/// Measures the specified text string when drawn with
|
||
/// the specified Font object and formatted with the
|
||
/// specified StringFormat object.
|
||
/// </summary>
|
||
/// <param name="text">The string to measure</param>
|
||
/// <param name="font">The Font object used to determine the size of the text string. </param>
|
||
/// <param name="layoutArea">A SizeF structure that specifies the layout rectangle for the text. </param>
|
||
/// <param name="stringFormat">A StringFormat object that represents formatting information, such as line spacing, for the text string. </param>
|
||
/// <returns>A SizeF structure that represents the size of text as drawn with font.</returns>
|
||
internal Size MeasureStringAbs( string text, Font font, SizeF layoutArea, StringFormat stringFormat )
|
||
{
|
||
SizeF size = this.MeasureString( text, font, layoutArea, stringFormat );
|
||
return new Size( (int)Math.Ceiling(size.Width), (int)Math.Ceiling(size.Height));
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draws the specified text string at the specified location
|
||
/// with the specified Brush object and font. The formatting
|
||
/// properties in the specified StringFormat object are applied
|
||
/// to the text.
|
||
/// </summary>
|
||
/// <param name="text">A string object that specifies the text to draw.</param>
|
||
/// <param name="font">A Font object that specifies the font face and size with which to draw the text.</param>
|
||
/// <param name="brush">A Brush object that determines the color and/or texture of the drawn text.</param>
|
||
/// <param name="layoutRectangle">A RectangleF structure that specifies the location of the drawn text.</param>
|
||
/// <param name="format">A StringFormat object that specifies formatting properties, such as line spacing and alignment, that are applied to the drawn text.</param>
|
||
internal void DrawStringRel( string text, Font font, Brush brush, RectangleF layoutRectangle, StringFormat format )
|
||
{
|
||
RectangleF rect;
|
||
|
||
// Check that rectangle is not empty
|
||
if(layoutRectangle.Width == 0 || layoutRectangle.Height == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// Get absolute coordinates
|
||
rect = GetAbsoluteRectangle( layoutRectangle );
|
||
|
||
// Draw text with anti-aliasing
|
||
/*
|
||
if( (this.AntiAliasing & AntiAliasing.Text) == AntiAliasing.Text )
|
||
{
|
||
this.TextRenderingHint = TextRenderingHint.AntiAlias;
|
||
}
|
||
else
|
||
{
|
||
this.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;
|
||
}
|
||
*/
|
||
|
||
this.DrawString( text, font, brush, rect, format );
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// Draws the specified text string at the specified location
|
||
/// with the specified angle and with the specified Brush object and font. The
|
||
/// formatting properties in the specified StringFormat object are applied
|
||
/// to the text.
|
||
/// </summary>
|
||
/// <param name="text">A string object that specifies the text to draw.</param>
|
||
/// <param name="font">A Font object that specifies the font face and size with which to draw the text.</param>
|
||
/// <param name="brush">A Brush object that determines the color and/or texture of the drawn text.</param>
|
||
/// <param name="layoutRectangle">A RectangleF structure that specifies the location of the drawn text.</param>
|
||
/// <param name="format">A StringFormat object that specifies formatting properties, such as line spacing and alignment, that are applied to the drawn text.</param>
|
||
/// <param name="angle">A angle of the text</param>
|
||
internal void DrawStringRel(
|
||
string text,
|
||
Font font,
|
||
Brush brush,
|
||
RectangleF layoutRectangle,
|
||
StringFormat format,
|
||
int angle
|
||
)
|
||
{
|
||
RectangleF rect;
|
||
SizeF size;
|
||
Matrix oldTransform;
|
||
PointF rotationCenter = PointF.Empty;
|
||
|
||
// Check that rectangle is not empty
|
||
if(layoutRectangle.Width == 0 || layoutRectangle.Height == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// Get absolute coordinates
|
||
rect = GetAbsoluteRectangle( layoutRectangle );
|
||
|
||
size = this.MeasureString( text, font, rect.Size, format );
|
||
|
||
|
||
// Find the center of rotation
|
||
if( format.Alignment == StringAlignment.Near )
|
||
{ // Near
|
||
rotationCenter.X = rect.X + size.Width / 2;
|
||
rotationCenter.Y = ( rect.Bottom + rect.Top ) / 2;
|
||
}
|
||
else if( format.Alignment == StringAlignment.Far )
|
||
{ // Far
|
||
rotationCenter.X = rect.Right - size.Width / 2;
|
||
rotationCenter.Y = ( rect.Bottom + rect.Top ) / 2;
|
||
}
|
||
else
|
||
{ // Center
|
||
rotationCenter.X = ( rect.Left + rect.Right ) / 2;
|
||
rotationCenter.Y = ( rect.Bottom + rect.Top ) / 2;
|
||
}
|
||
// Create a matrix and rotate it.
|
||
_myMatrix = this.Transform.Clone();
|
||
_myMatrix.RotateAt( angle, rotationCenter);
|
||
|
||
// Old angle
|
||
oldTransform = this.Transform;
|
||
|
||
// Set Angle
|
||
this.Transform = _myMatrix;
|
||
|
||
// Draw text with anti-aliasing
|
||
/*
|
||
if( (AntiAliasing & AntiAliasing.Text) == AntiAliasing.Text )
|
||
{
|
||
this.TextRenderingHint = TextRenderingHint.AntiAlias;
|
||
}
|
||
else
|
||
{
|
||
this.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;
|
||
}
|
||
*/
|
||
|
||
this.DrawString( text, font, brush, rect, format );
|
||
|
||
// Set Old Angle
|
||
this.Transform = oldTransform;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Rectangle Methods
|
||
|
||
/// <summary>
|
||
/// Draws different shadows to create bar styles.
|
||
/// </summary>
|
||
/// <param name="barDrawingStyle">Bar drawing style.</param>
|
||
/// <param name="isVertical">True if a vertical bar.</param>
|
||
/// <param name="rect">Rectangle position.</param>
|
||
internal void DrawRectangleBarStyle(BarDrawingStyle barDrawingStyle, bool isVertical, RectangleF rect)
|
||
{
|
||
// Check if non-default bar drawing style is specified
|
||
if(barDrawingStyle != BarDrawingStyle.Default)
|
||
{
|
||
// Check column/bar size
|
||
if(rect.Width > 0 && rect.Height > 0)
|
||
{
|
||
// Draw gradient(s)
|
||
if(barDrawingStyle == BarDrawingStyle.Cylinder)
|
||
{
|
||
// Calculate gradient position
|
||
RectangleF gradientRect = rect;
|
||
if(isVertical)
|
||
{
|
||
gradientRect.Width *= 0.3f;
|
||
}
|
||
else
|
||
{
|
||
gradientRect.Height *= 0.3f;
|
||
}
|
||
if(gradientRect.Width > 0 && gradientRect.Height > 0)
|
||
{
|
||
this.FillRectangleAbs(
|
||
gradientRect,
|
||
Color.Transparent,
|
||
ChartHatchStyle.None,
|
||
string.Empty,
|
||
ChartImageWrapMode.Scaled,
|
||
Color.Empty,
|
||
ChartImageAlignmentStyle.Center,
|
||
(isVertical) ? GradientStyle.LeftRight : GradientStyle.TopBottom,
|
||
Color.FromArgb(120, Color.White),
|
||
Color.Empty,
|
||
0,
|
||
ChartDashStyle.NotSet,
|
||
PenAlignment.Inset );
|
||
|
||
|
||
if(isVertical)
|
||
{
|
||
gradientRect.X += gradientRect.Width + 1f;
|
||
gradientRect.Width = rect.Right - gradientRect.X;
|
||
}
|
||
else
|
||
{
|
||
gradientRect.Y += gradientRect.Height + 1f;
|
||
gradientRect.Height = rect.Bottom - gradientRect.Y;
|
||
}
|
||
|
||
this.FillRectangleAbs(
|
||
gradientRect,
|
||
Color.FromArgb(120, Color.White),
|
||
ChartHatchStyle.None,
|
||
string.Empty,
|
||
ChartImageWrapMode.Scaled,
|
||
Color.Empty,
|
||
ChartImageAlignmentStyle.Center,
|
||
(isVertical) ? GradientStyle.LeftRight : GradientStyle.TopBottom,
|
||
Color.FromArgb(150, Color.Black),
|
||
Color.Empty,
|
||
0,
|
||
ChartDashStyle.NotSet,
|
||
PenAlignment.Inset );
|
||
|
||
}
|
||
}
|
||
else if(barDrawingStyle == BarDrawingStyle.Emboss)
|
||
{
|
||
// Calculate width of shadows used to create the effect
|
||
float shadowSize = 3f;
|
||
if(rect.Width < 6f || rect.Height < 6f)
|
||
{
|
||
shadowSize = 1f;
|
||
}
|
||
else if(rect.Width < 15f || rect.Height < 15f)
|
||
{
|
||
shadowSize = 2f;
|
||
}
|
||
|
||
// Create and draw left/top path
|
||
using(GraphicsPath path = new GraphicsPath())
|
||
{
|
||
// Add shadow polygon to the path
|
||
PointF[] points = new PointF[] {
|
||
new PointF(rect.Left, rect.Bottom),
|
||
new PointF(rect.Left, rect.Top),
|
||
new PointF(rect.Right, rect.Top),
|
||
new PointF(rect.Right - shadowSize, rect.Top + shadowSize),
|
||
new PointF(rect.Left + shadowSize, rect.Top + shadowSize),
|
||
new PointF(rect.Left + shadowSize, rect.Bottom - shadowSize) };
|
||
path.AddPolygon(points);
|
||
|
||
// Create brush
|
||
using(SolidBrush leftTopBrush = new SolidBrush(Color.FromArgb(100, Color.White)))
|
||
{
|
||
// Fill shadow path on the left-bottom side of the bar
|
||
this.FillPath(leftTopBrush, path);
|
||
}
|
||
}
|
||
|
||
// Create and draw top/right path
|
||
using(GraphicsPath path = new GraphicsPath())
|
||
{
|
||
// Add shadow polygon to the path
|
||
PointF[] points = new PointF[] {
|
||
new PointF(rect.Right, rect.Top),
|
||
new PointF(rect.Right, rect.Bottom),
|
||
new PointF(rect.Left, rect.Bottom),
|
||
new PointF(rect.Left + shadowSize, rect.Bottom - shadowSize),
|
||
new PointF(rect.Right - shadowSize, rect.Bottom - shadowSize),
|
||
new PointF(rect.Right - shadowSize, rect.Top + shadowSize) };
|
||
path.AddPolygon(points);
|
||
|
||
// Create brush
|
||
using(SolidBrush bottomRightBrush = new SolidBrush(Color.FromArgb(80, Color.Black)))
|
||
{
|
||
// Fill shadow path on the left-bottom side of the bar
|
||
this.FillPath(bottomRightBrush, path);
|
||
}
|
||
}
|
||
}
|
||
else if(barDrawingStyle == BarDrawingStyle.LightToDark)
|
||
{
|
||
// Calculate width of shadows used to create the effect
|
||
float shadowSize = 4f;
|
||
if(rect.Width < 6f || rect.Height < 6f)
|
||
{
|
||
shadowSize = 2f;
|
||
}
|
||
else if(rect.Width < 15f || rect.Height < 15f)
|
||
{
|
||
shadowSize = 3f;
|
||
}
|
||
|
||
// Calculate gradient position
|
||
RectangleF gradientRect = rect;
|
||
gradientRect.Inflate(-shadowSize, -shadowSize);
|
||
if(isVertical)
|
||
{
|
||
gradientRect.Height = (float)Math.Floor(gradientRect.Height / 3f);
|
||
}
|
||
else
|
||
{
|
||
gradientRect.X = gradientRect.Right - (float)Math.Floor(gradientRect.Width / 3f);
|
||
gradientRect.Width = (float)Math.Floor(gradientRect.Width / 3f);
|
||
}
|
||
if(gradientRect.Width > 0 && gradientRect.Height > 0)
|
||
{
|
||
this.FillRectangleAbs(
|
||
gradientRect,
|
||
(isVertical) ? Color.FromArgb(120, Color.White) : Color.Transparent,
|
||
ChartHatchStyle.None,
|
||
string.Empty,
|
||
ChartImageWrapMode.Scaled,
|
||
Color.Empty,
|
||
ChartImageAlignmentStyle.Center,
|
||
(isVertical) ? GradientStyle.TopBottom : GradientStyle.LeftRight,
|
||
(isVertical) ? Color.Transparent : Color.FromArgb(120, Color.White),
|
||
Color.Empty,
|
||
0,
|
||
ChartDashStyle.NotSet,
|
||
PenAlignment.Inset );
|
||
|
||
gradientRect = rect;
|
||
gradientRect.Inflate(-shadowSize, -shadowSize);
|
||
if(isVertical)
|
||
{
|
||
gradientRect.Y = gradientRect.Bottom - (float)Math.Floor(gradientRect.Height / 3f);
|
||
gradientRect.Height = (float)Math.Floor(gradientRect.Height / 3f);
|
||
}
|
||
else
|
||
{
|
||
gradientRect.Width = (float)Math.Floor(gradientRect.Width / 3f);
|
||
}
|
||
|
||
|
||
this.FillRectangleAbs(
|
||
gradientRect,
|
||
(!isVertical) ? Color.FromArgb(80, Color.Black) : Color.Transparent,
|
||
ChartHatchStyle.None,
|
||
string.Empty,
|
||
ChartImageWrapMode.Scaled,
|
||
Color.Empty,
|
||
ChartImageAlignmentStyle.Center,
|
||
(isVertical) ? GradientStyle.TopBottom : GradientStyle.LeftRight,
|
||
(!isVertical) ? Color.Transparent : Color.FromArgb(80, Color.Black),
|
||
Color.Empty,
|
||
0,
|
||
ChartDashStyle.NotSet,
|
||
PenAlignment.Inset );
|
||
|
||
}
|
||
}
|
||
else if(barDrawingStyle == BarDrawingStyle.Wedge)
|
||
{
|
||
// Calculate wedge size to fit the rectangle
|
||
float size = (isVertical) ? rect.Width / 2f : rect.Height / 2f;
|
||
if(isVertical && 2f * size > rect.Height)
|
||
{
|
||
size = rect.Height/2f;
|
||
}
|
||
if(!isVertical && 2f * size > rect.Width)
|
||
{
|
||
size = rect.Width/2f;
|
||
}
|
||
|
||
// Draw left/bottom shadow
|
||
RectangleF gradientRect = rect;
|
||
using(GraphicsPath path = new GraphicsPath())
|
||
{
|
||
if(isVertical)
|
||
{
|
||
path.AddLine(gradientRect.X + gradientRect.Width/2f, gradientRect.Y + size, gradientRect.X + gradientRect.Width/2f, gradientRect.Bottom - size);
|
||
path.AddLine(gradientRect.X + gradientRect.Width/2f, gradientRect.Bottom - size, gradientRect.Right, gradientRect.Bottom);
|
||
path.AddLine(gradientRect.Right, gradientRect.Bottom, gradientRect.Right, gradientRect.Y);
|
||
}
|
||
else
|
||
{
|
||
path.AddLine(gradientRect.X + size, gradientRect.Y + gradientRect.Height/2f, gradientRect.Right - size, gradientRect.Y + gradientRect.Height/2f);
|
||
path.AddLine(gradientRect.Right - size, gradientRect.Y + gradientRect.Height/2f, gradientRect.Right, gradientRect.Bottom);
|
||
path.AddLine(gradientRect.Right, gradientRect.Bottom, gradientRect.Left, gradientRect.Bottom);
|
||
}
|
||
path.CloseAllFigures();
|
||
|
||
// Create brush and fill path
|
||
using(SolidBrush brush = new SolidBrush(Color.FromArgb(90, Color.Black)))
|
||
{
|
||
this.FillPath(brush, path);
|
||
}
|
||
}
|
||
|
||
// Draw top/right triangle
|
||
using(GraphicsPath path = new GraphicsPath())
|
||
{
|
||
if(isVertical)
|
||
{
|
||
path.AddLine(gradientRect.X, gradientRect.Y, gradientRect.X + gradientRect.Width/2f, gradientRect.Y + size);
|
||
path.AddLine(gradientRect.X + gradientRect.Width/2f, gradientRect.Y + size, gradientRect.Right, gradientRect.Y);
|
||
}
|
||
else
|
||
{
|
||
path.AddLine(gradientRect.Right, gradientRect.Y, gradientRect.Right - size, gradientRect.Y + gradientRect.Height / 2f);
|
||
path.AddLine(gradientRect.Right - size, gradientRect.Y + gradientRect.Height / 2f, gradientRect.Right, gradientRect.Bottom);
|
||
}
|
||
|
||
// Create brush and fill path
|
||
using(SolidBrush brush = new SolidBrush(Color.FromArgb(50, Color.Black)))
|
||
{
|
||
// Fill shadow path on the left-bottom side of the bar
|
||
this.FillPath(brush, path);
|
||
|
||
// Draw Lines
|
||
using(Pen penDark = new Pen(Color.FromArgb(20, Color.Black), 1))
|
||
{
|
||
this.DrawPath(penDark, path);
|
||
if(isVertical)
|
||
{
|
||
this.DrawLine(
|
||
penDark,
|
||
rect.X + rect.Width/2f,
|
||
rect.Y + size,
|
||
rect.X + rect.Width/2f,
|
||
rect.Bottom - size);
|
||
}
|
||
else
|
||
{
|
||
this.DrawLine(
|
||
penDark,
|
||
rect.X + size,
|
||
rect.Y + rect.Height/2f,
|
||
rect.X + size,
|
||
rect.Bottom - rect.Height/2f);
|
||
}
|
||
}
|
||
|
||
// Draw Lines
|
||
using(Pen pen = new Pen(Color.FromArgb(40, Color.White), 1))
|
||
{
|
||
this.DrawPath(pen, path);
|
||
if(isVertical)
|
||
{
|
||
this.DrawLine(
|
||
pen,
|
||
rect.X + rect.Width/2f,
|
||
rect.Y + size,
|
||
rect.X + rect.Width/2f,
|
||
rect.Bottom - size);
|
||
}
|
||
else
|
||
{
|
||
this.DrawLine(
|
||
pen,
|
||
rect.X + size,
|
||
rect.Y + rect.Height/2f,
|
||
rect.X + size,
|
||
rect.Bottom - rect.Height/2f);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Draw bottom/left triangle
|
||
using(GraphicsPath path = new GraphicsPath())
|
||
{
|
||
if(isVertical)
|
||
{
|
||
path.AddLine(gradientRect.X, gradientRect.Bottom, gradientRect.X + gradientRect.Width/2f, gradientRect.Bottom - size);
|
||
path.AddLine(gradientRect.X + gradientRect.Width/2f, gradientRect.Bottom - size, gradientRect.Right, gradientRect.Bottom);
|
||
}
|
||
else
|
||
{
|
||
path.AddLine(gradientRect.X, gradientRect.Y, gradientRect.X + size, gradientRect.Y + gradientRect.Height / 2f);
|
||
path.AddLine(gradientRect.X + size, gradientRect.Y + gradientRect.Height / 2f, gradientRect.X, gradientRect.Bottom);
|
||
}
|
||
|
||
// Create brush
|
||
using(SolidBrush brush = new SolidBrush(Color.FromArgb(50, Color.Black)))
|
||
{
|
||
// Fill shadow path on the left-bottom side of the bar
|
||
this.FillPath(brush, path);
|
||
|
||
// Draw edges
|
||
using(Pen penDark = new Pen(Color.FromArgb(20, Color.Black), 1))
|
||
{
|
||
this.DrawPath(penDark, path);
|
||
}
|
||
using(Pen pen = new Pen(Color.FromArgb(40, Color.White), 1))
|
||
{
|
||
this.DrawPath(pen, path);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draw a bar with shadow.
|
||
/// </summary>
|
||
/// <param name="rectF">Size of rectangle</param>
|
||
/// <param name="backColor">Color of rectangle</param>
|
||
/// <param name="backHatchStyle">Hatch style</param>
|
||
/// <param name="backImage">Back Image</param>
|
||
/// <param name="backImageWrapMode">Image mode</param>
|
||
/// <param name="backImageTransparentColor">Image transparent color.</param>
|
||
/// <param name="backImageAlign">Image alignment</param>
|
||
/// <param name="backGradientStyle">Gradient type </param>
|
||
/// <param name="backSecondaryColor">Gradient End Color</param>
|
||
/// <param name="borderColor">Border Color</param>
|
||
/// <param name="borderWidth">Border Width</param>
|
||
/// <param name="borderDashStyle">Border Style</param>
|
||
/// <param name="shadowColor">Shadow Color</param>
|
||
/// <param name="shadowOffset">Shadow Offset</param>
|
||
/// <param name="penAlignment">Pen Alignment</param>
|
||
/// <param name="barDrawingStyle">Bar drawing style.</param>
|
||
/// <param name="isVertical">True if a vertical bar.</param>
|
||
internal void FillRectangleRel( RectangleF rectF,
|
||
Color backColor,
|
||
ChartHatchStyle backHatchStyle,
|
||
string backImage,
|
||
ChartImageWrapMode backImageWrapMode,
|
||
Color backImageTransparentColor,
|
||
ChartImageAlignmentStyle backImageAlign,
|
||
GradientStyle backGradientStyle,
|
||
Color backSecondaryColor,
|
||
Color borderColor,
|
||
int borderWidth,
|
||
ChartDashStyle borderDashStyle,
|
||
Color shadowColor,
|
||
int shadowOffset,
|
||
PenAlignment penAlignment,
|
||
BarDrawingStyle barDrawingStyle,
|
||
bool isVertical)
|
||
{
|
||
this.FillRectangleRel(
|
||
rectF,
|
||
backColor,
|
||
backHatchStyle,
|
||
backImage,
|
||
backImageWrapMode,
|
||
backImageTransparentColor,
|
||
backImageAlign,
|
||
backGradientStyle,
|
||
backSecondaryColor,
|
||
borderColor,
|
||
borderWidth,
|
||
borderDashStyle,
|
||
shadowColor,
|
||
shadowOffset,
|
||
penAlignment,
|
||
false,
|
||
0,
|
||
false,
|
||
barDrawingStyle,
|
||
isVertical);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draw a bar with shadow.
|
||
/// </summary>
|
||
/// <param name="rectF">Size of rectangle</param>
|
||
/// <param name="backColor">Color of rectangle</param>
|
||
/// <param name="backHatchStyle">Hatch style</param>
|
||
/// <param name="backImage">Back Image</param>
|
||
/// <param name="backImageWrapMode">Image mode</param>
|
||
/// <param name="backImageTransparentColor">Image transparent color.</param>
|
||
/// <param name="backImageAlign">Image alignment</param>
|
||
/// <param name="backGradientStyle">Gradient type </param>
|
||
/// <param name="backSecondaryColor">Gradient End Color</param>
|
||
/// <param name="borderColor">Border Color</param>
|
||
/// <param name="borderWidth">Border Width</param>
|
||
/// <param name="borderDashStyle">Border Style</param>
|
||
/// <param name="shadowColor">Shadow Color</param>
|
||
/// <param name="shadowOffset">Shadow Offset</param>
|
||
/// <param name="penAlignment">Pen Alignment</param>
|
||
internal void FillRectangleRel( RectangleF rectF,
|
||
Color backColor,
|
||
ChartHatchStyle backHatchStyle,
|
||
string backImage,
|
||
ChartImageWrapMode backImageWrapMode,
|
||
Color backImageTransparentColor,
|
||
ChartImageAlignmentStyle backImageAlign,
|
||
GradientStyle backGradientStyle,
|
||
Color backSecondaryColor,
|
||
Color borderColor,
|
||
int borderWidth,
|
||
ChartDashStyle borderDashStyle,
|
||
Color shadowColor,
|
||
int shadowOffset,
|
||
PenAlignment penAlignment )
|
||
{
|
||
this.FillRectangleRel(
|
||
rectF,
|
||
backColor,
|
||
backHatchStyle,
|
||
backImage,
|
||
backImageWrapMode,
|
||
backImageTransparentColor,
|
||
backImageAlign,
|
||
backGradientStyle,
|
||
backSecondaryColor,
|
||
borderColor,
|
||
borderWidth,
|
||
borderDashStyle,
|
||
shadowColor,
|
||
shadowOffset,
|
||
penAlignment,
|
||
false,
|
||
0,
|
||
false,
|
||
BarDrawingStyle.Default,
|
||
true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draws rectangle or circle (inside rectangle) with shadow.
|
||
/// </summary>
|
||
/// <param name="rectF">Size of rectangle</param>
|
||
/// <param name="backColor">Color of rectangle</param>
|
||
/// <param name="backHatchStyle">Hatch style</param>
|
||
/// <param name="backImage">Back Image</param>
|
||
/// <param name="backImageWrapMode">Image mode</param>
|
||
/// <param name="backImageTransparentColor">Image transparent color.</param>
|
||
/// <param name="backImageAlign">Image alignment</param>
|
||
/// <param name="backGradientStyle">Gradient type </param>
|
||
/// <param name="backSecondaryColor">Gradient End Color</param>
|
||
/// <param name="borderColor">Border Color</param>
|
||
/// <param name="borderWidth">Border Width</param>
|
||
/// <param name="borderDashStyle">Border Style</param>
|
||
/// <param name="shadowColor">Shadow Color</param>
|
||
/// <param name="shadowOffset">Shadow Offset</param>
|
||
/// <param name="penAlignment">Pen Alignment</param>
|
||
/// <param name="circular">Draw circular shape inside the rectangle.</param>
|
||
/// <param name="circularSectorsCount">Number of sectors in circle when drawing the polygon.</param>
|
||
/// <param name="circle3D">3D Circle must be drawn.</param>
|
||
internal void FillRectangleRel( RectangleF rectF,
|
||
Color backColor,
|
||
ChartHatchStyle backHatchStyle,
|
||
string backImage,
|
||
ChartImageWrapMode backImageWrapMode,
|
||
Color backImageTransparentColor,
|
||
ChartImageAlignmentStyle backImageAlign,
|
||
GradientStyle backGradientStyle,
|
||
Color backSecondaryColor,
|
||
Color borderColor,
|
||
int borderWidth,
|
||
ChartDashStyle borderDashStyle,
|
||
Color shadowColor,
|
||
int shadowOffset,
|
||
PenAlignment penAlignment,
|
||
bool circular,
|
||
int circularSectorsCount,
|
||
bool circle3D)
|
||
{
|
||
this.FillRectangleRel(
|
||
rectF,
|
||
backColor,
|
||
backHatchStyle,
|
||
backImage,
|
||
backImageWrapMode,
|
||
backImageTransparentColor,
|
||
backImageAlign,
|
||
backGradientStyle,
|
||
backSecondaryColor,
|
||
borderColor,
|
||
borderWidth,
|
||
borderDashStyle,
|
||
shadowColor,
|
||
shadowOffset,
|
||
penAlignment,
|
||
circular,
|
||
circularSectorsCount,
|
||
circle3D,
|
||
BarDrawingStyle.Default,
|
||
true);
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// Draws rectangle or circle (inside rectangle) with shadow.
|
||
/// </summary>
|
||
/// <param name="rectF">Size of rectangle</param>
|
||
/// <param name="backColor">Color of rectangle</param>
|
||
/// <param name="backHatchStyle">Hatch style</param>
|
||
/// <param name="backImage">Back Image</param>
|
||
/// <param name="backImageWrapMode">Image mode</param>
|
||
/// <param name="backImageTransparentColor">Image transparent color.</param>
|
||
/// <param name="backImageAlign">Image alignment</param>
|
||
/// <param name="backGradientStyle">Gradient type </param>
|
||
/// <param name="backSecondaryColor">Gradient End Color</param>
|
||
/// <param name="borderColor">Border Color</param>
|
||
/// <param name="borderWidth">Border Width</param>
|
||
/// <param name="borderDashStyle">Border Style</param>
|
||
/// <param name="shadowColor">Shadow Color</param>
|
||
/// <param name="shadowOffset">Shadow Offset</param>
|
||
/// <param name="penAlignment">Pen Alignment</param>
|
||
/// <param name="circular">Draw circular shape inside the rectangle.</param>
|
||
/// <param name="circularSectorsCount">Number of sectors in circle when drawing the polygon.</param>
|
||
/// <param name="circle3D">3D Circle must be drawn.</param>
|
||
/// <param name="barDrawingStyle">Bar drawing style.</param>
|
||
/// <param name="isVertical">True if a vertical bar.</param>
|
||
internal void FillRectangleRel( RectangleF rectF,
|
||
Color backColor,
|
||
ChartHatchStyle backHatchStyle,
|
||
string backImage,
|
||
ChartImageWrapMode backImageWrapMode,
|
||
Color backImageTransparentColor,
|
||
ChartImageAlignmentStyle backImageAlign,
|
||
GradientStyle backGradientStyle,
|
||
Color backSecondaryColor,
|
||
Color borderColor,
|
||
int borderWidth,
|
||
ChartDashStyle borderDashStyle,
|
||
Color shadowColor,
|
||
int shadowOffset,
|
||
PenAlignment penAlignment,
|
||
bool circular,
|
||
int circularSectorsCount,
|
||
bool circle3D,
|
||
BarDrawingStyle barDrawingStyle,
|
||
bool isVertical)
|
||
{
|
||
Brush brush = null;
|
||
Brush backBrush = null;
|
||
|
||
// Remember SmoothingMode and turn off anti aliasing
|
||
SmoothingMode oldSmoothingMode = this.SmoothingMode;
|
||
if(!circular)
|
||
{
|
||
this.SmoothingMode = SmoothingMode.Default;
|
||
}
|
||
|
||
// Color is empty
|
||
if( backColor.IsEmpty )
|
||
{
|
||
backColor = Color.White;
|
||
}
|
||
|
||
if( backSecondaryColor.IsEmpty )
|
||
{
|
||
backSecondaryColor = Color.White;
|
||
}
|
||
|
||
if( borderColor.IsEmpty || borderDashStyle == ChartDashStyle.NotSet)
|
||
{
|
||
borderWidth = 0;
|
||
}
|
||
|
||
// Get absolute coordinates
|
||
RectangleF rect = GetAbsoluteRectangle( rectF );
|
||
|
||
// Rectangle width and height can not be very small value
|
||
if( rect.Width < 1.0F && rect.Width > 0.0F )
|
||
{
|
||
rect.Width = 1.0F;
|
||
}
|
||
|
||
if( rect.Height < 1.0F && rect.Height > 0.0F )
|
||
{
|
||
rect.Height = 1.0F;
|
||
}
|
||
|
||
// Round the values
|
||
rect = Round( rect );
|
||
|
||
// For inset alignment resize fill rectangle
|
||
RectangleF fillRect;
|
||
if( penAlignment == PenAlignment.Inset &&
|
||
borderWidth > 0)
|
||
{
|
||
// SVG and Metafiles do not support inset pen styles - use same rectangle
|
||
if( this.ActiveRenderingType == RenderingType.Svg ||
|
||
this.IsMetafile)
|
||
{
|
||
fillRect = new RectangleF( rect.X, rect.Y, rect.Width, rect.Height);
|
||
}
|
||
else if (this.Graphics.Transform.Elements[0] != 1f ||
|
||
this.Graphics.Transform.Elements[3] != 1f)
|
||
{
|
||
// Do not reduce filling rectangle if scaling is used in the graphics
|
||
// transformations. Rounding may cause a 1 pixel gap between the border
|
||
// and the filling.
|
||
fillRect = new RectangleF( rect.X, rect.Y, rect.Width, rect.Height);
|
||
}
|
||
else
|
||
{
|
||
// The fill rectangle is resized because of border size.
|
||
fillRect = new RectangleF(
|
||
rect.X + borderWidth,
|
||
rect.Y + borderWidth,
|
||
rect.Width - borderWidth * 2f + 1,
|
||
rect.Height - borderWidth * 2f + 1);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// The fill rectangle is same
|
||
fillRect = rect;
|
||
}
|
||
|
||
// Fix for issue #6714:
|
||
// Make sure the rectangle coordinates fit the control. In same cases rectangle width or
|
||
// hight ca be extremly large. Drawing such a rectangle may cause an overflow exception.
|
||
// The code below restricts the maximum size to double the chart size. See issue
|
||
// description for more information. -AG.
|
||
if(fillRect.Width > 2f * this._width)
|
||
{
|
||
fillRect.Width = 2f * this._width;
|
||
}
|
||
if(fillRect.Height > 2f * this._height)
|
||
{
|
||
fillRect.Height = 2f * this._height;
|
||
}
|
||
|
||
|
||
if( backImage.Length > 0 && backImageWrapMode != ChartImageWrapMode.Unscaled && backImageWrapMode != ChartImageWrapMode.Scaled)
|
||
{
|
||
backBrush = brush;
|
||
brush = GetTextureBrush(backImage, backImageTransparentColor, backImageWrapMode, backColor);
|
||
}
|
||
else if( backHatchStyle != ChartHatchStyle.None )
|
||
{
|
||
brush = GetHatchBrush( backHatchStyle, backColor, backSecondaryColor );
|
||
}
|
||
else if( backGradientStyle != GradientStyle.None )
|
||
{
|
||
// If a gradient type is set create a brush with gradient
|
||
brush = GetGradientBrush( rect, backColor, backSecondaryColor, backGradientStyle );
|
||
}
|
||
else
|
||
{
|
||
// Set a bar color.
|
||
if(backColor == Color.Empty || backColor == Color.Transparent)
|
||
{
|
||
brush = null;
|
||
}
|
||
else
|
||
{
|
||
brush = new SolidBrush(backColor);
|
||
}
|
||
}
|
||
|
||
// Draw shadow
|
||
FillRectangleShadowAbs( rect, shadowColor, shadowOffset, backColor, circular, circularSectorsCount );
|
||
|
||
// Draw rectangle image
|
||
if( backImage.Length > 0 && (backImageWrapMode == ChartImageWrapMode.Unscaled || backImageWrapMode == ChartImageWrapMode.Scaled))
|
||
{
|
||
// Load image
|
||
System.Drawing.Image image = _common.ImageLoader.LoadImage( backImage );
|
||
|
||
// Prepare image properties (transparent color)
|
||
ImageAttributes attrib = new ImageAttributes();
|
||
if(backImageTransparentColor != Color.Empty)
|
||
{
|
||
attrib.SetColorKey(backImageTransparentColor, backImageTransparentColor, ColorAdjustType.Default);
|
||
}
|
||
|
||
// Draw scaled image
|
||
RectangleF imageRect = new RectangleF();
|
||
imageRect.X = fillRect.X;
|
||
imageRect.Y = fillRect.Y;
|
||
imageRect.Width = fillRect.Width;
|
||
imageRect.Height = fillRect.Height;
|
||
|
||
SizeF imageAbsSize = new SizeF();
|
||
|
||
// Calculate unscaled image position
|
||
if(backImageWrapMode == ChartImageWrapMode.Unscaled)
|
||
{
|
||
ImageLoader.GetAdjustedImageSize(image, this.Graphics, ref imageAbsSize);
|
||
|
||
// Calculate image position
|
||
imageRect.Width = Math.Min(fillRect.Width, imageAbsSize.Width);
|
||
imageRect.Height = Math.Min(fillRect.Height, imageAbsSize.Height);
|
||
|
||
// Adjust position with alignment property
|
||
if(imageRect.Width < fillRect.Width)
|
||
{
|
||
if(backImageAlign == ChartImageAlignmentStyle.BottomRight ||
|
||
backImageAlign == ChartImageAlignmentStyle.Right ||
|
||
backImageAlign == ChartImageAlignmentStyle.TopRight)
|
||
{
|
||
imageRect.X = fillRect.Right - imageRect.Width;
|
||
}
|
||
else if(backImageAlign == ChartImageAlignmentStyle.Bottom ||
|
||
backImageAlign == ChartImageAlignmentStyle.Center ||
|
||
backImageAlign == ChartImageAlignmentStyle.Top)
|
||
{
|
||
imageRect.X = fillRect.X + (fillRect.Width - imageRect.Width)/2;
|
||
}
|
||
}
|
||
if(imageRect.Height < fillRect.Height)
|
||
{
|
||
if(backImageAlign == ChartImageAlignmentStyle.BottomRight ||
|
||
backImageAlign == ChartImageAlignmentStyle.Bottom ||
|
||
backImageAlign == ChartImageAlignmentStyle.BottomLeft)
|
||
{
|
||
imageRect.Y = fillRect.Bottom - imageRect.Height;
|
||
}
|
||
else if(backImageAlign == ChartImageAlignmentStyle.Left ||
|
||
backImageAlign == ChartImageAlignmentStyle.Center ||
|
||
backImageAlign == ChartImageAlignmentStyle.Right)
|
||
{
|
||
imageRect.Y = fillRect.Y + (fillRect.Height - imageRect.Height)/2;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
// Fill background with brush
|
||
if(brush != null)
|
||
{
|
||
if(circular)
|
||
this.DrawCircleAbs( null, brush, fillRect, circularSectorsCount, circle3D );
|
||
else
|
||
this.FillRectangle( brush, fillRect );
|
||
}
|
||
|
||
// Draw image
|
||
this.DrawImage(image,
|
||
new Rectangle((int)Math.Round(imageRect.X),(int)Math.Round(imageRect.Y), (int)Math.Round(imageRect.Width), (int)Math.Round(imageRect.Height)),
|
||
0, 0,
|
||
(backImageWrapMode == ChartImageWrapMode.Unscaled) ? imageRect.Width * image.Width / imageAbsSize.Width : image.Width,
|
||
(backImageWrapMode == ChartImageWrapMode.Unscaled) ? imageRect.Height * image.Height / imageAbsSize.Height : image.Height,
|
||
GraphicsUnit.Pixel,
|
||
attrib);
|
||
}
|
||
// Draw rectangle
|
||
else
|
||
{
|
||
if(backBrush != null && backImageTransparentColor != Color.Empty)
|
||
{
|
||
// Fill background with brush
|
||
if(circular)
|
||
this.DrawCircleAbs( null, backBrush, fillRect, circularSectorsCount, circle3D );
|
||
else
|
||
this.FillRectangle( backBrush, fillRect );
|
||
}
|
||
|
||
if(brush != null)
|
||
{
|
||
if(circular)
|
||
this.DrawCircleAbs( null, brush, fillRect, circularSectorsCount, circle3D );
|
||
else
|
||
this.FillRectangle( brush, fillRect );
|
||
}
|
||
}
|
||
|
||
// Draw different bar style
|
||
this.DrawRectangleBarStyle(barDrawingStyle, isVertical, fillRect);
|
||
|
||
// Draw border
|
||
if( borderWidth > 0 && borderDashStyle != ChartDashStyle.NotSet)
|
||
{
|
||
// Set a border line color
|
||
if(_pen.Color != borderColor)
|
||
{
|
||
_pen.Color = borderColor;
|
||
}
|
||
|
||
// Set a border line width
|
||
if(_pen.Width != borderWidth)
|
||
{
|
||
_pen.Width = borderWidth;
|
||
}
|
||
|
||
// Set pen alignment
|
||
if(_pen.Alignment != penAlignment)
|
||
{
|
||
_pen.Alignment = penAlignment;
|
||
}
|
||
|
||
// Set a border line style
|
||
if(_pen.DashStyle != GetPenStyle( borderDashStyle ))
|
||
{
|
||
_pen.DashStyle = GetPenStyle( borderDashStyle );
|
||
}
|
||
|
||
// Draw border
|
||
if(circular)
|
||
{
|
||
this.DrawCircleAbs( _pen, null, rect, circularSectorsCount, false );
|
||
}
|
||
else
|
||
{
|
||
// NOTE: Rectangle with single pixel inset border is drawn 1 pixel larger
|
||
// in the .Net Framework. Increase size by 1 pixel to solve the issue.
|
||
if(_pen.Alignment == PenAlignment.Inset && _pen.Width > 1f)
|
||
{
|
||
rect.Width += 1;
|
||
rect.Height += 1;
|
||
}
|
||
|
||
// Draw rectangle
|
||
this.DrawRectangle( _pen, rect.X, rect.Y, rect.Width, rect.Height );
|
||
}
|
||
}
|
||
|
||
// Dispose Image and Gradient
|
||
if(brush != null)
|
||
{
|
||
brush.Dispose();
|
||
}
|
||
|
||
// Return old smoothing mode
|
||
this.SmoothingMode = oldSmoothingMode;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draw Shadow for a bar
|
||
/// </summary>
|
||
/// <param name="rect">Bar rectangle</param>
|
||
/// <param name="shadowColor">Shadow Color</param>
|
||
/// <param name="shadowOffset">Shadow Offset</param>
|
||
/// <param name="backColor">Back Color</param>
|
||
internal void FillRectangleShadowAbs(
|
||
RectangleF rect,
|
||
Color shadowColor,
|
||
float shadowOffset,
|
||
Color backColor)
|
||
{
|
||
FillRectangleShadowAbs(
|
||
rect,
|
||
shadowColor,
|
||
shadowOffset,
|
||
backColor,
|
||
false,
|
||
0);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draw Shadow for a bar
|
||
/// </summary>
|
||
/// <param name="rect">Bar rectangle</param>
|
||
/// <param name="shadowColor">Shadow Color</param>
|
||
/// <param name="shadowOffset">Shadow Offset</param>
|
||
/// <param name="backColor">Back Color</param>
|
||
/// <param name="circular">Draw circular shape inside the rectangle.</param>
|
||
/// <param name="circularSectorsCount">Number of sectors in circle when drawing the polygon.</param>
|
||
internal void FillRectangleShadowAbs(
|
||
RectangleF rect,
|
||
Color shadowColor,
|
||
float shadowOffset,
|
||
Color backColor,
|
||
bool circular,
|
||
int circularSectorsCount)
|
||
{
|
||
// Do not draw shadoe for empty rectangle
|
||
if(rect.Height == 0 || rect.Width == 0 || shadowOffset == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// Do not draw shadow if color is IsEmpty or offset is 0
|
||
if (shadowOffset == 0 || shadowColor == Color.Empty)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// For non-circualr shadow with transparent background - use clipping
|
||
bool clippingUsed = false;
|
||
Region oldClipRegion = null;
|
||
if (!circular && backColor == Color.Transparent)
|
||
{
|
||
clippingUsed = true;
|
||
oldClipRegion = this.Clip;
|
||
Region region = new Region();
|
||
region.MakeInfinite();
|
||
region.Xor(rect);
|
||
this.Clip = region;
|
||
}
|
||
|
||
// Draw usual or "soft" shadows
|
||
if(!softShadows || circularSectorsCount > 2)
|
||
{
|
||
RectangleF absolute;
|
||
RectangleF offset = RectangleF.Empty;
|
||
|
||
absolute = Round( rect );
|
||
|
||
// Change shadow color
|
||
using (SolidBrush shadowBrush = new SolidBrush((shadowColor.A != 255) ? shadowColor : Color.FromArgb(backColor.A / 2, shadowColor)))
|
||
{
|
||
// Shadow Position
|
||
offset.X = absolute.X + shadowOffset;
|
||
offset.Y = absolute.Y + shadowOffset;
|
||
offset.Width = absolute.Width;
|
||
offset.Height = absolute.Height;
|
||
|
||
// Draw rectangle
|
||
if (circular)
|
||
this.DrawCircleAbs(null, shadowBrush, offset, circularSectorsCount, false);
|
||
else
|
||
this.FillRectangle(shadowBrush, offset);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
|
||
RectangleF absolute;
|
||
RectangleF offset = RectangleF.Empty;
|
||
|
||
absolute = Round( rect );
|
||
|
||
|
||
// Shadow Position
|
||
offset.X = absolute.X + shadowOffset - 1;
|
||
offset.Y = absolute.Y + shadowOffset - 1;
|
||
offset.Width = absolute.Width + 2;
|
||
offset.Height = absolute.Height + 2;
|
||
|
||
// Calculate rounded rect radius
|
||
float radius = shadowOffset * 0.7f;
|
||
radius = (float)Math.Max(radius, 2f);
|
||
radius = (float)Math.Min(radius, offset.Width/4f);
|
||
radius = (float)Math.Min(radius, offset.Height/4f);
|
||
radius = (float)Math.Ceiling(radius);
|
||
if(circular)
|
||
{
|
||
radius = offset.Width/2f;
|
||
}
|
||
|
||
// Create rounded rectangle path
|
||
GraphicsPath path = new GraphicsPath();
|
||
if(circular && offset.Width != offset.Height)
|
||
{
|
||
float radiusX = offset.Width/2f;
|
||
float radiusY = offset.Height/2f;
|
||
path.AddLine(offset.X+radiusX, offset.Y, offset.Right-radiusX, offset.Y);
|
||
path.AddArc(offset.Right-2f*radiusX, offset.Y, 2f*radiusX, 2f*radiusY, 270, 90);
|
||
path.AddLine(offset.Right, offset.Y + radiusY, offset.Right, offset.Bottom - radiusY);
|
||
path.AddArc(offset.Right-2f*radiusX, offset.Bottom-2f*radiusY, 2f*radiusX, 2f*radiusY, 0, 90);
|
||
path.AddLine(offset.Right-radiusX, offset.Bottom, offset.X + radiusX, offset.Bottom);
|
||
path.AddArc(offset.X, offset.Bottom-2f*radiusY, 2f*radiusX, 2f*radiusY, 90, 90);
|
||
path.AddLine(offset.X, offset.Bottom-radiusY, offset.X, offset.Y+radiusY);
|
||
path.AddArc(offset.X, offset.Y, 2f*radiusX, 2f*radiusY, 180, 90);
|
||
}
|
||
else
|
||
{
|
||
path.AddLine(offset.X+radius, offset.Y, offset.Right-radius, offset.Y);
|
||
path.AddArc(offset.Right-2f*radius, offset.Y, 2f*radius, 2f*radius, 270, 90);
|
||
path.AddLine(offset.Right, offset.Y + radius, offset.Right, offset.Bottom - radius);
|
||
path.AddArc(offset.Right-2f*radius, offset.Bottom-2f*radius, 2f*radius, 2f*radius, 0, 90);
|
||
path.AddLine(offset.Right-radius, offset.Bottom, offset.X + radius, offset.Bottom);
|
||
path.AddArc(offset.X, offset.Bottom-2f*radius, 2f*radius, 2f*radius, 90, 90);
|
||
path.AddLine(offset.X, offset.Bottom-radius, offset.X, offset.Y+radius);
|
||
path.AddArc(offset.X, offset.Y, 2f*radius, 2f*radius, 180, 90);
|
||
}
|
||
|
||
PathGradientBrush shadowBrush = new PathGradientBrush(path);
|
||
shadowBrush.CenterColor = shadowColor;
|
||
|
||
// Set the color along the entire boundary of the path
|
||
Color[] colors = {Color.Transparent};
|
||
shadowBrush.SurroundColors = colors;
|
||
shadowBrush.CenterPoint = new PointF(offset.X + offset.Width/2f, offset.Y + offset.Height/2f);
|
||
|
||
// Define brush focus scale
|
||
PointF focusScale = new PointF(1-2f*shadowOffset/offset.Width, 1-2f*shadowOffset/offset.Height);
|
||
if(focusScale.X < 0)
|
||
focusScale.X = 0;
|
||
if(focusScale.Y < 0)
|
||
focusScale.Y = 0;
|
||
shadowBrush.FocusScales = focusScale;
|
||
|
||
// Draw rectangle
|
||
this.FillPath(shadowBrush, path);
|
||
}
|
||
|
||
// Reset clip region
|
||
if (clippingUsed)
|
||
{
|
||
Region region = this.Clip;
|
||
this.Clip = oldClipRegion;
|
||
region.Dispose();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Gets the path of the polygon which represent the circular area.
|
||
/// </summary>
|
||
/// <param name="position">Circle position.</param>
|
||
/// <param name="polygonSectorsNumber">Number of sectors for the polygon.</param>
|
||
/// <returns>Graphics path of the polygon circle.</returns>
|
||
internal GraphicsPath GetPolygonCirclePath(RectangleF position, int polygonSectorsNumber)
|
||
{
|
||
PointF firstPoint = new PointF(position.X + position.Width/2f, position.Y);
|
||
PointF centerPoint = new PointF(position.X + position.Width/2f, position.Y + position.Height/2f);
|
||
float sectorSize = 0f;
|
||
GraphicsPath path = new GraphicsPath();
|
||
PointF prevPoint = PointF.Empty;
|
||
float curentSector = 0f;
|
||
|
||
// Get sector size
|
||
if(polygonSectorsNumber <= 2)
|
||
{
|
||
// Circle sector size
|
||
sectorSize = 1f;
|
||
}
|
||
else
|
||
{
|
||
// Polygon sector size
|
||
sectorSize = 360f / ((float)polygonSectorsNumber);
|
||
}
|
||
|
||
// Loop throug all sectors
|
||
for(curentSector = 0f; curentSector < 360f; curentSector += sectorSize)
|
||
{
|
||
// Create matrix
|
||
Matrix matrix = new Matrix();
|
||
matrix.RotateAt(curentSector, centerPoint);
|
||
|
||
// Get point and rotate it
|
||
PointF[] points = new PointF[] { firstPoint };
|
||
matrix.TransformPoints(points);
|
||
|
||
// Add point into the path
|
||
if(!prevPoint.IsEmpty)
|
||
{
|
||
path.AddLine(prevPoint, points[0]);
|
||
}
|
||
|
||
// Remember last point
|
||
prevPoint = points[0];
|
||
}
|
||
|
||
path.CloseAllFigures();
|
||
|
||
return path;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Fills and/or draws border as circle or polygon.
|
||
/// </summary>
|
||
/// <param name="pen">Border pen.</param>
|
||
/// <param name="brush">Border brush.</param>
|
||
/// <param name="position">Circle position.</param>
|
||
/// <param name="polygonSectorsNumber">Number of sectors for the polygon.</param>
|
||
/// <param name="circle3D">Indicates that circle should be 3D..</param>
|
||
internal void DrawCircleAbs(Pen pen, Brush brush, RectangleF position, int polygonSectorsNumber, bool circle3D)
|
||
{
|
||
bool fill3DCircle = (circle3D && brush != null);
|
||
|
||
// Draw 2D circle
|
||
if(polygonSectorsNumber <= 2 && !fill3DCircle)
|
||
{
|
||
if(brush != null)
|
||
{
|
||
this.FillEllipse(brush, position);
|
||
}
|
||
if(pen != null)
|
||
{
|
||
this.DrawEllipse(pen, position);
|
||
}
|
||
}
|
||
|
||
// Draw circle as polygon with specified number of sectors
|
||
else
|
||
{
|
||
PointF firstPoint = new PointF(position.X + position.Width/2f, position.Y);
|
||
PointF centerPoint = new PointF(position.X + position.Width/2f, position.Y + position.Height/2f);
|
||
float sectorSize = 0f;
|
||
PointF prevPoint = PointF.Empty;
|
||
float curentSector = 0f;
|
||
|
||
using (GraphicsPath path = new GraphicsPath())
|
||
{
|
||
// Remember current smoothing mode
|
||
SmoothingMode oldMode = this.SmoothingMode;
|
||
if (fill3DCircle)
|
||
{
|
||
this.SmoothingMode = SmoothingMode.None;
|
||
}
|
||
|
||
// Get sector size
|
||
if (polygonSectorsNumber <= 2)
|
||
{
|
||
// Circle sector size
|
||
sectorSize = 1f;
|
||
}
|
||
else
|
||
{
|
||
// Polygon sector size
|
||
sectorSize = 360f / ((float)polygonSectorsNumber);
|
||
}
|
||
|
||
// Loop throug all sectors
|
||
for (curentSector = 0f; curentSector < 360f; curentSector += sectorSize)
|
||
{
|
||
// Create matrix
|
||
Matrix matrix = new Matrix();
|
||
matrix.RotateAt(curentSector, centerPoint);
|
||
|
||
// Get point and rotate it
|
||
PointF[] points = new PointF[] { firstPoint };
|
||
matrix.TransformPoints(points);
|
||
|
||
// Add point into the path
|
||
if (!prevPoint.IsEmpty)
|
||
{
|
||
path.AddLine(prevPoint, points[0]);
|
||
|
||
// Fill each segment separatly for the 3D look
|
||
if (fill3DCircle)
|
||
{
|
||
path.AddLine(points[0], centerPoint);
|
||
path.AddLine(centerPoint, prevPoint);
|
||
using (Brush sectorBrush = GetSector3DBrush(brush, curentSector, sectorSize))
|
||
{
|
||
this.FillPath(sectorBrush, path);
|
||
}
|
||
path.Reset();
|
||
}
|
||
}
|
||
|
||
// Remember last point
|
||
prevPoint = points[0];
|
||
}
|
||
|
||
path.CloseAllFigures();
|
||
|
||
// Fill last segment for the 3D look
|
||
if (!prevPoint.IsEmpty && fill3DCircle)
|
||
{
|
||
path.AddLine(prevPoint, firstPoint);
|
||
path.AddLine(firstPoint, centerPoint);
|
||
path.AddLine(centerPoint, prevPoint);
|
||
using (Brush sectorBrush = GetSector3DBrush(brush, curentSector, sectorSize))
|
||
{
|
||
this.FillPath(sectorBrush, path);
|
||
}
|
||
path.Reset();
|
||
}
|
||
|
||
// Restore old mode
|
||
if (fill3DCircle)
|
||
{
|
||
this.SmoothingMode = oldMode;
|
||
}
|
||
|
||
if (brush != null && !circle3D)
|
||
{
|
||
this.FillPath(brush, path);
|
||
}
|
||
if (pen != null)
|
||
{
|
||
this.DrawPath(pen, path);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates 3D sector brush.
|
||
/// </summary>
|
||
/// <param name="brush">Original brush.</param>
|
||
/// <param name="curentSector">Sector position.</param>
|
||
/// <param name="sectorSize">Sector size.</param>
|
||
/// <returns>3D brush.</returns>
|
||
[SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily",
|
||
Justification = "Too large of a code change to justify making this change")]
|
||
internal Brush GetSector3DBrush(Brush brush, float curentSector, float sectorSize)
|
||
{
|
||
// Get color from the brush
|
||
Color brushColor = Color.Gray;
|
||
if(brush is HatchBrush)
|
||
{
|
||
brushColor = ((HatchBrush)brush).BackgroundColor;
|
||
}
|
||
else if(brush is LinearGradientBrush)
|
||
{
|
||
brushColor = ((LinearGradientBrush)brush).LinearColors[0];
|
||
}
|
||
else if(brush is PathGradientBrush)
|
||
{
|
||
brushColor = ((PathGradientBrush)brush).CenterColor;
|
||
}
|
||
else if(brush is SolidBrush)
|
||
{
|
||
brushColor = ((SolidBrush)brush).Color;
|
||
}
|
||
|
||
// Adjust sector angle
|
||
curentSector -= sectorSize / 2f;
|
||
|
||
// Make adjustment for polygon circle with 5 segments
|
||
// to avoid the issue that bottom segment is too dark
|
||
if(sectorSize == 72f && curentSector == 180f)
|
||
{
|
||
curentSector *= 0.8f;
|
||
}
|
||
|
||
// No angles more than 180
|
||
if(curentSector > 180)
|
||
{
|
||
curentSector = 360f - curentSector;
|
||
}
|
||
curentSector = curentSector / 180F;
|
||
|
||
// Get brush
|
||
brushColor = GetBrightGradientColor( brushColor, curentSector);
|
||
|
||
// Get brush
|
||
return new SolidBrush(brushColor);
|
||
}
|
||
|
||
/// <summary>
|
||
/// This method creates gradient color with brightness
|
||
/// </summary>
|
||
/// <param name="beginColor">Start color for gradient.</param>
|
||
/// <param name="position">Position used between Start and end color.</param>
|
||
/// <returns>Calculated Gradient color from gradient position</returns>
|
||
internal Color GetBrightGradientColor( Color beginColor, double position )
|
||
{
|
||
double brightness = 0.5;
|
||
if( position < brightness )
|
||
{
|
||
return GetGradientColor( Color.FromArgb(beginColor.A,255,255,255), beginColor, 1 - brightness + position );
|
||
}
|
||
else if( -brightness + position < 1 )
|
||
{
|
||
return GetGradientColor( beginColor, Color.Black, -brightness + position);
|
||
}
|
||
else
|
||
{
|
||
return Color.FromArgb( beginColor.A, 0, 0, 0 );
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draw Rectangle using absolute coordinates.
|
||
/// </summary>
|
||
/// <param name="rect">Size of rectangle</param>
|
||
/// <param name="backColor">Color of rectangle</param>
|
||
/// <param name="backHatchStyle">Hatch Style</param>
|
||
/// <param name="backImage">Image URL</param>
|
||
/// <param name="backImageWrapMode">Image Mode</param>
|
||
/// <param name="backImageTransparentColor">Image transparent color.</param>
|
||
/// <param name="backImageAlign">Image alignment.</param>
|
||
/// <param name="backGradientStyle">Gradient AxisName</param>
|
||
/// <param name="backSecondaryColor">End Gradient color</param>
|
||
/// <param name="borderColor">Border Color</param>
|
||
/// <param name="borderWidth">Border Width</param>
|
||
/// <param name="borderDashStyle">Border Style</param>
|
||
/// <param name="penAlignment">Border is outside or inside rectangle</param>
|
||
internal void FillRectangleAbs( RectangleF rect,
|
||
Color backColor,
|
||
ChartHatchStyle backHatchStyle,
|
||
string backImage,
|
||
ChartImageWrapMode backImageWrapMode,
|
||
Color backImageTransparentColor,
|
||
ChartImageAlignmentStyle backImageAlign,
|
||
GradientStyle backGradientStyle,
|
||
Color backSecondaryColor,
|
||
Color borderColor,
|
||
int borderWidth,
|
||
ChartDashStyle borderDashStyle,
|
||
PenAlignment penAlignment )
|
||
{
|
||
Brush brush = null;
|
||
Brush backBrush = null;
|
||
|
||
// Turn off Antialias
|
||
SmoothingMode oldMode = this.SmoothingMode;
|
||
this.SmoothingMode = SmoothingMode.None;
|
||
|
||
// Color is empty
|
||
if( backColor.IsEmpty )
|
||
backColor = Color.White;
|
||
|
||
if( backSecondaryColor.IsEmpty )
|
||
backSecondaryColor = Color.White;
|
||
|
||
if( borderColor.IsEmpty )
|
||
{
|
||
borderColor = Color.White;
|
||
borderWidth = 0;
|
||
}
|
||
|
||
// Set a border line color
|
||
_pen.Color = borderColor;
|
||
|
||
// Set a border line width
|
||
_pen.Width = borderWidth;
|
||
|
||
// Set pen alignment
|
||
_pen.Alignment = penAlignment;
|
||
|
||
// Set a border line style
|
||
_pen.DashStyle = GetPenStyle( borderDashStyle );
|
||
|
||
if( backGradientStyle == GradientStyle.None )
|
||
{
|
||
// Set a bar color.
|
||
_solidBrush.Color = backColor;
|
||
brush = _solidBrush;
|
||
}
|
||
else
|
||
{
|
||
// If a gradient type is set create a brush with gradient
|
||
brush = GetGradientBrush( rect, backColor, backSecondaryColor, backGradientStyle );
|
||
}
|
||
|
||
if( backHatchStyle != ChartHatchStyle.None )
|
||
{
|
||
brush = GetHatchBrush( backHatchStyle, backColor, backSecondaryColor );
|
||
}
|
||
|
||
if( backImage.Length > 0 && backImageWrapMode != ChartImageWrapMode.Unscaled && backImageWrapMode != ChartImageWrapMode.Scaled)
|
||
{
|
||
backBrush = brush;
|
||
brush = GetTextureBrush(backImage, backImageTransparentColor, backImageWrapMode, backColor );
|
||
}
|
||
|
||
// For inset alignment resize fill rectangle
|
||
RectangleF fillRect;
|
||
|
||
// The fill rectangle is same
|
||
fillRect = new RectangleF( rect.X + borderWidth, rect.Y + borderWidth, rect.Width - borderWidth * 2, rect.Height - borderWidth * 2 );
|
||
|
||
// FillRectangle and DrawRectangle works differently with RectangleF.
|
||
fillRect.Width += 1;
|
||
fillRect.Height += 1;
|
||
|
||
// Draw rectangle image
|
||
if( backImage.Length > 0 && (backImageWrapMode == ChartImageWrapMode.Unscaled || backImageWrapMode == ChartImageWrapMode.Scaled))
|
||
{
|
||
// Load image
|
||
System.Drawing.Image image = _common.ImageLoader.LoadImage( backImage );
|
||
|
||
|
||
// Prepare image properties (transparent color)
|
||
ImageAttributes attrib = new ImageAttributes();
|
||
if(backImageTransparentColor != Color.Empty)
|
||
{
|
||
attrib.SetColorKey(backImageTransparentColor, backImageTransparentColor, ColorAdjustType.Default);
|
||
}
|
||
|
||
// Draw scaled image
|
||
RectangleF imageRect = new RectangleF();
|
||
imageRect.X = fillRect.X;
|
||
imageRect.Y = fillRect.Y;
|
||
imageRect.Width = fillRect.Width;
|
||
imageRect.Height = fillRect.Height;
|
||
|
||
// Draw unscaled image using align property
|
||
if(backImageWrapMode == ChartImageWrapMode.Unscaled)
|
||
{
|
||
SizeF imageAbsSize = new SizeF();
|
||
|
||
ImageLoader.GetAdjustedImageSize(image, this.Graphics, ref imageAbsSize);
|
||
|
||
// Calculate image position
|
||
imageRect.Width = imageAbsSize.Width;
|
||
imageRect.Height = imageAbsSize.Height;
|
||
|
||
// Adjust position with alignment property
|
||
if(imageRect.Width < fillRect.Width)
|
||
{
|
||
if(backImageAlign == ChartImageAlignmentStyle.BottomRight ||
|
||
backImageAlign == ChartImageAlignmentStyle.Right ||
|
||
backImageAlign == ChartImageAlignmentStyle.TopRight)
|
||
{
|
||
imageRect.X = fillRect.Right - imageRect.Width;
|
||
}
|
||
else if(backImageAlign == ChartImageAlignmentStyle.Bottom ||
|
||
backImageAlign == ChartImageAlignmentStyle.Center ||
|
||
backImageAlign == ChartImageAlignmentStyle.Top)
|
||
{
|
||
imageRect.X = fillRect.X + (fillRect.Width - imageRect.Width)/2;
|
||
}
|
||
}
|
||
if(imageRect.Height < fillRect.Height)
|
||
{
|
||
if(backImageAlign == ChartImageAlignmentStyle.BottomRight ||
|
||
backImageAlign == ChartImageAlignmentStyle.Bottom ||
|
||
backImageAlign == ChartImageAlignmentStyle.BottomLeft)
|
||
{
|
||
imageRect.Y = fillRect.Bottom - imageRect.Height;
|
||
}
|
||
else if(backImageAlign == ChartImageAlignmentStyle.Left ||
|
||
backImageAlign == ChartImageAlignmentStyle.Center ||
|
||
backImageAlign == ChartImageAlignmentStyle.Right)
|
||
{
|
||
imageRect.Y = fillRect.Y + (fillRect.Height - imageRect.Height)/2;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
// Fill background with brush
|
||
this.FillRectangle( brush, rect.X, rect.Y, rect.Width + 1, rect.Height + 1);
|
||
|
||
// Draw image
|
||
this.DrawImage(image,
|
||
new Rectangle((int)Math.Round(imageRect.X),(int)Math.Round(imageRect.Y), (int)Math.Round(imageRect.Width), (int)Math.Round(imageRect.Height)),
|
||
0, 0, image.Width, image.Height,
|
||
GraphicsUnit.Pixel,
|
||
attrib);
|
||
}
|
||
// Draw rectangle
|
||
else
|
||
{
|
||
if(backBrush != null && backImageTransparentColor != Color.Empty)
|
||
{
|
||
// Fill background with brush
|
||
this.FillRectangle( backBrush, rect.X, rect.Y, rect.Width + 1, rect.Height + 1 );
|
||
}
|
||
this.FillRectangle( brush, rect.X, rect.Y, rect.Width + 1, rect.Height + 1 );
|
||
}
|
||
|
||
// Set pen alignment
|
||
if(borderDashStyle != ChartDashStyle.NotSet)
|
||
{
|
||
if( borderWidth > 1 )
|
||
this.DrawRectangle( _pen, rect.X, rect.Y, rect.Width + 1, rect.Height + 1 );
|
||
else if( borderWidth == 1 )
|
||
this.DrawRectangle( _pen, rect.X, rect.Y, rect.Width, rect.Height );
|
||
}
|
||
|
||
// Dispose Image and Gradient
|
||
if( backGradientStyle != GradientStyle.None )
|
||
{
|
||
brush.Dispose();
|
||
}
|
||
if( backImage.Length > 0 && backImageWrapMode != ChartImageWrapMode.Unscaled && backImageWrapMode != ChartImageWrapMode.Scaled)
|
||
{
|
||
brush.Dispose();
|
||
}
|
||
if( backHatchStyle != ChartHatchStyle.None )
|
||
{
|
||
brush.Dispose();
|
||
}
|
||
|
||
// Set Old Smoothing Mode
|
||
this.SmoothingMode = oldMode;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Fills graphics path with shadow using absolute coordinates.
|
||
/// </summary>
|
||
/// <param name="path">Graphics path to fill.</param>
|
||
/// <param name="backColor">Color of rectangle</param>
|
||
/// <param name="backHatchStyle">Hatch Style</param>
|
||
/// <param name="backImage">Image URL</param>
|
||
/// <param name="backImageWrapMode">Image Mode</param>
|
||
/// <param name="backImageTransparentColor">Image transparent color.</param>
|
||
/// <param name="backImageAlign">Image alignment.</param>
|
||
/// <param name="backGradientStyle">Gradient AxisName</param>
|
||
/// <param name="backSecondaryColor">End Gradient color</param>
|
||
/// <param name="borderColor">Border Color</param>
|
||
/// <param name="borderWidth">Border Width</param>
|
||
/// <param name="borderDashStyle">Border Style</param>
|
||
/// <param name="penAlignment">Border is outside or inside rectangle</param>
|
||
/// <param name="shadowOffset">Shadow offset.</param>
|
||
/// <param name="shadowColor">Shadow color.</param>
|
||
internal void DrawPathAbs(
|
||
GraphicsPath path,
|
||
Color backColor,
|
||
ChartHatchStyle backHatchStyle,
|
||
string backImage,
|
||
ChartImageWrapMode backImageWrapMode,
|
||
Color backImageTransparentColor,
|
||
ChartImageAlignmentStyle backImageAlign,
|
||
GradientStyle backGradientStyle,
|
||
Color backSecondaryColor,
|
||
Color borderColor,
|
||
int borderWidth,
|
||
ChartDashStyle borderDashStyle,
|
||
PenAlignment penAlignment,
|
||
int shadowOffset,
|
||
Color shadowColor)
|
||
{
|
||
// Draw patj shadow
|
||
if(shadowOffset != 0 && shadowColor != Color.Transparent)
|
||
{
|
||
// Save graphics state and apply translate transformation
|
||
GraphicsState graphicsState = this.Save();
|
||
this.TranslateTransform(shadowOffset, shadowOffset);
|
||
|
||
if(backColor == Color.Transparent &&
|
||
backSecondaryColor.IsEmpty )
|
||
{
|
||
this.DrawPathAbs(
|
||
path,
|
||
Color.Transparent,
|
||
ChartHatchStyle.None,
|
||
String.Empty,
|
||
ChartImageWrapMode.Scaled,
|
||
Color.Empty,
|
||
ChartImageAlignmentStyle.Center,
|
||
GradientStyle.None,
|
||
Color.Empty,
|
||
shadowColor,
|
||
borderWidth,
|
||
borderDashStyle,
|
||
PenAlignment.Center);
|
||
}
|
||
else
|
||
{
|
||
this.DrawPathAbs(
|
||
path,
|
||
shadowColor,
|
||
ChartHatchStyle.None,
|
||
String.Empty,
|
||
ChartImageWrapMode.Scaled,
|
||
Color.Empty,
|
||
ChartImageAlignmentStyle.Center,
|
||
GradientStyle.None,
|
||
Color.Empty,
|
||
Color.Transparent,
|
||
0,
|
||
ChartDashStyle.NotSet,
|
||
PenAlignment.Center);
|
||
}
|
||
|
||
// Restore graphics state
|
||
this.Restore(graphicsState);
|
||
}
|
||
|
||
// Draw path
|
||
this.DrawPathAbs(
|
||
path,
|
||
backColor,
|
||
backHatchStyle,
|
||
backImage,
|
||
backImageWrapMode,
|
||
backImageTransparentColor,
|
||
backImageAlign,
|
||
backGradientStyle,
|
||
backSecondaryColor,
|
||
borderColor,
|
||
borderWidth,
|
||
borderDashStyle,
|
||
penAlignment);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Fills graphics path using absolute coordinates.
|
||
/// </summary>
|
||
/// <param name="path">Graphics path to fill.</param>
|
||
/// <param name="backColor">Color of rectangle</param>
|
||
/// <param name="backHatchStyle">Hatch Style</param>
|
||
/// <param name="backImage">Image URL</param>
|
||
/// <param name="backImageWrapMode">Image Mode</param>
|
||
/// <param name="backImageTransparentColor">Image transparent color.</param>
|
||
/// <param name="backImageAlign">Image alignment.</param>
|
||
/// <param name="backGradientStyle">Gradient AxisName</param>
|
||
/// <param name="backSecondaryColor">End Gradient color</param>
|
||
/// <param name="borderColor">Border Color</param>
|
||
/// <param name="borderWidth">Border Width</param>
|
||
/// <param name="borderDashStyle">Border Style</param>
|
||
/// <param name="penAlignment">Border is outside or inside rectangle</param>
|
||
internal void DrawPathAbs( GraphicsPath path,
|
||
Color backColor,
|
||
ChartHatchStyle backHatchStyle,
|
||
string backImage,
|
||
ChartImageWrapMode backImageWrapMode,
|
||
Color backImageTransparentColor,
|
||
ChartImageAlignmentStyle backImageAlign,
|
||
GradientStyle backGradientStyle,
|
||
Color backSecondaryColor,
|
||
Color borderColor,
|
||
int borderWidth,
|
||
ChartDashStyle borderDashStyle,
|
||
PenAlignment penAlignment )
|
||
{
|
||
Brush brush = null;
|
||
Brush backBrush = null;
|
||
|
||
// Color is empty
|
||
if( backColor.IsEmpty )
|
||
backColor = Color.White;
|
||
|
||
if( backSecondaryColor.IsEmpty )
|
||
backSecondaryColor = Color.White;
|
||
|
||
if( borderColor.IsEmpty )
|
||
{
|
||
borderColor = Color.White;
|
||
borderWidth = 0;
|
||
}
|
||
|
||
// Set pen properties
|
||
_pen.Color = borderColor;
|
||
_pen.Width = borderWidth;
|
||
_pen.Alignment = penAlignment;
|
||
_pen.DashStyle = GetPenStyle( borderDashStyle );
|
||
|
||
if( backGradientStyle == GradientStyle.None )
|
||
{
|
||
// Set solid brush color.
|
||
_solidBrush.Color = backColor;
|
||
brush = _solidBrush;
|
||
}
|
||
else
|
||
{
|
||
// If a gradient type is set create a brush with gradient
|
||
RectangleF pathRect = path.GetBounds();
|
||
pathRect.Inflate(new SizeF(2,2));
|
||
brush = GetGradientBrush(
|
||
pathRect,
|
||
backColor,
|
||
backSecondaryColor,
|
||
backGradientStyle );
|
||
}
|
||
|
||
if( backHatchStyle != ChartHatchStyle.None )
|
||
{
|
||
brush = GetHatchBrush( backHatchStyle, backColor, backSecondaryColor );
|
||
}
|
||
|
||
if( backImage.Length > 0 && backImageWrapMode != ChartImageWrapMode.Unscaled && backImageWrapMode != ChartImageWrapMode.Scaled)
|
||
{
|
||
backBrush = brush;
|
||
brush = GetTextureBrush(backImage, backImageTransparentColor, backImageWrapMode, backColor );
|
||
}
|
||
|
||
// For inset alignment resize fill rectangle
|
||
RectangleF fillRect = path.GetBounds();
|
||
|
||
// Draw rectangle image
|
||
if( backImage.Length > 0 && (backImageWrapMode == ChartImageWrapMode.Unscaled || backImageWrapMode == ChartImageWrapMode.Scaled))
|
||
{
|
||
// Load image
|
||
System.Drawing.Image image = _common.ImageLoader.LoadImage( backImage );
|
||
|
||
// Prepare image properties (transparent color)
|
||
ImageAttributes attrib = new ImageAttributes();
|
||
if(backImageTransparentColor != Color.Empty)
|
||
{
|
||
attrib.SetColorKey(backImageTransparentColor, backImageTransparentColor, ColorAdjustType.Default);
|
||
}
|
||
|
||
// Draw scaled image
|
||
RectangleF imageRect = new RectangleF();
|
||
imageRect.X = fillRect.X;
|
||
imageRect.Y = fillRect.Y;
|
||
imageRect.Width = fillRect.Width;
|
||
imageRect.Height = fillRect.Height;
|
||
|
||
// Draw unscaled image using align property
|
||
if(backImageWrapMode == ChartImageWrapMode.Unscaled)
|
||
{
|
||
SizeF imageSize = new SizeF();
|
||
|
||
ImageLoader.GetAdjustedImageSize(image, this.Graphics, ref imageSize);
|
||
|
||
// Calculate image position
|
||
imageRect.Width = imageSize.Width;
|
||
imageRect.Height = imageSize.Height;
|
||
|
||
// Adjust position with alignment property
|
||
if(imageRect.Width < fillRect.Width)
|
||
{
|
||
if(backImageAlign == ChartImageAlignmentStyle.BottomRight ||
|
||
backImageAlign == ChartImageAlignmentStyle.Right ||
|
||
backImageAlign == ChartImageAlignmentStyle.TopRight)
|
||
{
|
||
imageRect.X = fillRect.Right - imageRect.Width;
|
||
}
|
||
else if(backImageAlign == ChartImageAlignmentStyle.Bottom ||
|
||
backImageAlign == ChartImageAlignmentStyle.Center ||
|
||
backImageAlign == ChartImageAlignmentStyle.Top)
|
||
{
|
||
imageRect.X = fillRect.X + (fillRect.Width - imageRect.Width)/2;
|
||
}
|
||
}
|
||
if(imageRect.Height < fillRect.Height)
|
||
{
|
||
if(backImageAlign == ChartImageAlignmentStyle.BottomRight ||
|
||
backImageAlign == ChartImageAlignmentStyle.Bottom ||
|
||
backImageAlign == ChartImageAlignmentStyle.BottomLeft)
|
||
{
|
||
imageRect.Y = fillRect.Bottom - imageRect.Height;
|
||
}
|
||
else if(backImageAlign == ChartImageAlignmentStyle.Left ||
|
||
backImageAlign == ChartImageAlignmentStyle.Center ||
|
||
backImageAlign == ChartImageAlignmentStyle.Right)
|
||
{
|
||
imageRect.Y = fillRect.Y + (fillRect.Height - imageRect.Height)/2;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
// Fill background with brush
|
||
this.FillPath( brush, path );
|
||
|
||
// Draw image
|
||
Region oldClipRegion = this.Clip;
|
||
this.Clip = new Region(path);
|
||
this.DrawImage(image,
|
||
new Rectangle((int)Math.Round(imageRect.X),(int)Math.Round(imageRect.Y), (int)Math.Round(imageRect.Width), (int)Math.Round(imageRect.Height)),
|
||
0, 0, image.Width, image.Height,
|
||
GraphicsUnit.Pixel,
|
||
attrib);
|
||
this.Clip = oldClipRegion;
|
||
}
|
||
|
||
// Draw rectangle
|
||
else
|
||
{
|
||
if(backBrush != null && backImageTransparentColor != Color.Empty)
|
||
{
|
||
// Fill background with brush
|
||
this.FillPath( backBrush, path);
|
||
}
|
||
this.FillPath( brush, path);
|
||
}
|
||
|
||
// Draw border
|
||
if(borderColor != Color.Empty && borderWidth > 0 && borderDashStyle != ChartDashStyle.NotSet)
|
||
{
|
||
this.DrawPath( _pen, path );
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates brush with specified properties.
|
||
/// </summary>
|
||
/// <param name="rect">Gradient rectangle</param>
|
||
/// <param name="backColor">Color of rectangle</param>
|
||
/// <param name="backHatchStyle">Hatch style</param>
|
||
/// <param name="backImage">Back Image</param>
|
||
/// <param name="backImageWrapMode">Image mode</param>
|
||
/// <param name="backImageTransparentColor">Image transparent color.</param>
|
||
/// <param name="backGradientStyle">Gradient type </param>
|
||
/// <param name="backSecondaryColor">Gradient End Color</param>
|
||
/// <returns>New brush object.</returns>
|
||
internal Brush CreateBrush(
|
||
RectangleF rect,
|
||
Color backColor,
|
||
ChartHatchStyle backHatchStyle,
|
||
string backImage,
|
||
ChartImageWrapMode backImageWrapMode,
|
||
Color backImageTransparentColor,
|
||
GradientStyle backGradientStyle,
|
||
Color backSecondaryColor
|
||
)
|
||
{
|
||
Brush brush = new SolidBrush(backColor);
|
||
|
||
if( backImage.Length > 0 && backImageWrapMode != ChartImageWrapMode.Unscaled && backImageWrapMode != ChartImageWrapMode.Scaled)
|
||
{
|
||
brush = GetTextureBrush(backImage, backImageTransparentColor, backImageWrapMode, backColor );
|
||
}
|
||
else if( backHatchStyle != ChartHatchStyle.None )
|
||
{
|
||
brush = GetHatchBrush( backHatchStyle, backColor, backSecondaryColor );
|
||
}
|
||
else if( backGradientStyle != GradientStyle.None )
|
||
{
|
||
// If a gradient type is set create a brush with gradient
|
||
brush = GetGradientBrush( rect, backColor, backSecondaryColor, backGradientStyle );
|
||
}
|
||
|
||
return brush;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Coordinates converter
|
||
|
||
/// <summary>
|
||
/// This method takes a RectangleF structure that is using absolute coordinates
|
||
/// and returns a RectangleF object that uses relative coordinates.
|
||
/// </summary>
|
||
/// <param name="rectangle">RectangleF structure in absolute coordinates.</param>
|
||
/// <returns>RectangleF structure in relative coordinates.</returns>
|
||
public RectangleF GetRelativeRectangle( RectangleF rectangle )
|
||
{
|
||
// Check arguments
|
||
if (rectangle == null)
|
||
throw new ArgumentNullException("rectangle");
|
||
|
||
RectangleF relative = RectangleF.Empty;
|
||
|
||
// Convert absolute coordinates to relative coordinates
|
||
relative.X = rectangle.X * 100F / ((float)(_width - 1));
|
||
relative.Y = rectangle.Y * 100F / ((float)(_height - 1));
|
||
relative.Width = rectangle.Width * 100F / ((float)(_width - 1));
|
||
relative.Height = rectangle.Height * 100F / ((float)(_height - 1));
|
||
|
||
// Return Relative coordinates
|
||
return relative;
|
||
}
|
||
|
||
/// <summary>
|
||
/// This method takes a PointF object that is using absolute coordinates
|
||
/// and returns a PointF object that uses relative coordinates.
|
||
/// </summary>
|
||
/// <param name="point">PointF object in absolute coordinates.</param>
|
||
/// <returns>PointF object in relative coordinates.</returns>
|
||
public PointF GetRelativePoint( PointF point )
|
||
{
|
||
// Check arguments
|
||
if (point == null)
|
||
throw new ArgumentNullException("point");
|
||
|
||
PointF relative = PointF.Empty;
|
||
|
||
// Convert absolute coordinates to relative coordinates
|
||
relative.X = point.X * 100F / ((float)(_width - 1));
|
||
relative.Y = point.Y * 100F / ((float)(_height - 1));
|
||
|
||
// Return Relative coordinates
|
||
return relative;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// This method takes a SizeF object that uses absolute coordinates
|
||
/// and returns a SizeF object that uses relative coordinates.
|
||
/// </summary>
|
||
/// <param name="size">SizeF object in absolute coordinates.</param>
|
||
/// <returns>SizeF object in relative coordinates.</returns>
|
||
public SizeF GetRelativeSize( SizeF size )
|
||
{
|
||
// Check arguments
|
||
if (size == null)
|
||
throw new ArgumentNullException("size");
|
||
|
||
SizeF relative = SizeF.Empty;
|
||
|
||
// Convert absolute coordinates to relative coordinates
|
||
relative.Width = size.Width * 100F / ((float)(_width - 1));
|
||
relative.Height = size.Height * 100F / ((float)(_height - 1));
|
||
|
||
// Return relative coordinates
|
||
return relative;
|
||
}
|
||
|
||
/// <summary>
|
||
/// This method takes a PointF object and converts its relative coordinates
|
||
/// to absolute coordinates.
|
||
/// </summary>
|
||
/// <param name="point">PointF object in relative coordinates.</param>
|
||
/// <returns>PointF object in absolute coordinates.</returns>
|
||
public PointF GetAbsolutePoint( PointF point )
|
||
{
|
||
// Check arguments
|
||
if (point == null)
|
||
throw new ArgumentNullException("point");
|
||
|
||
PointF absolute = PointF.Empty;
|
||
|
||
// Convert relative coordinates to absolute coordinates
|
||
absolute.X = point.X * (_width - 1) / 100F;
|
||
absolute.Y = point.Y * (_height - 1) / 100F;
|
||
|
||
// Return Absolute coordinates
|
||
return absolute;
|
||
}
|
||
|
||
/// <summary>
|
||
/// This method takes a RectangleF structure and converts its relative coordinates
|
||
/// to absolute coordinates.
|
||
/// </summary>
|
||
/// <param name="rectangle">RectangleF object in relative coordinates.</param>
|
||
/// <returns>RectangleF object in absolute coordinates.</returns>
|
||
public RectangleF GetAbsoluteRectangle( RectangleF rectangle )
|
||
{
|
||
// Check arguments
|
||
if (rectangle == null)
|
||
throw new ArgumentNullException("rectangle");
|
||
|
||
RectangleF absolute = RectangleF.Empty;
|
||
|
||
// Convert relative coordinates to absolute coordinates
|
||
absolute.X = rectangle.X * (_width - 1) / 100F;
|
||
absolute.Y = rectangle.Y * (_height - 1) / 100F;
|
||
absolute.Width = rectangle.Width * (_width - 1) / 100F;
|
||
absolute.Height = rectangle.Height * (_height - 1) / 100F;
|
||
|
||
// Return Absolute coordinates
|
||
return absolute;
|
||
}
|
||
|
||
/// <summary>
|
||
/// This method takes a SizeF object that uses relative coordinates
|
||
/// and returns a SizeF object that uses absolute coordinates.
|
||
/// </summary>
|
||
/// <param name="size">SizeF object in relative coordinates.</param>
|
||
/// <returns>SizeF object in absolute coordinates.</returns>
|
||
public SizeF GetAbsoluteSize( SizeF size )
|
||
{
|
||
// Check arguments
|
||
if (size == null)
|
||
throw new ArgumentNullException("size");
|
||
|
||
SizeF absolute = SizeF.Empty;
|
||
|
||
// Convert relative coordinates to absolute coordinates
|
||
absolute.Width = size.Width * (_width - 1) / 100F;
|
||
absolute.Height = size.Height * (_height - 1) / 100F;
|
||
|
||
// Return Absolute coordinates
|
||
return absolute;
|
||
}
|
||
|
||
|
||
#endregion
|
||
|
||
#region Border drawing helper methods
|
||
|
||
/// <summary>
|
||
/// Helper function which creates a rounded rectangle path.
|
||
/// </summary>
|
||
/// <param name="rect">Rectangle coordinates.</param>
|
||
/// <param name="cornerRadius">Array of 4 corners radius.</param>
|
||
/// <returns>Graphics path object.</returns>
|
||
internal GraphicsPath CreateRoundedRectPath(RectangleF rect, float[] cornerRadius)
|
||
{
|
||
// Create rounded rectangle path
|
||
GraphicsPath path = new GraphicsPath();
|
||
path.AddLine(rect.X+cornerRadius[0], rect.Y, rect.Right-cornerRadius[1], rect.Y);
|
||
path.AddArc(rect.Right-2f*cornerRadius[1], rect.Y, 2f*cornerRadius[1], 2f*cornerRadius[2], 270, 90);
|
||
path.AddLine(rect.Right, rect.Y + cornerRadius[2], rect.Right, rect.Bottom - cornerRadius[3]);
|
||
path.AddArc(rect.Right-2f*cornerRadius[4], rect.Bottom-2f*cornerRadius[3], 2f*cornerRadius[4], 2f*cornerRadius[3], 0, 90);
|
||
path.AddLine(rect.Right-cornerRadius[4], rect.Bottom, rect.X + cornerRadius[5], rect.Bottom);
|
||
path.AddArc(rect.X, rect.Bottom-2f*cornerRadius[6], 2f*cornerRadius[5], 2f*cornerRadius[6], 90, 90);
|
||
path.AddLine(rect.X, rect.Bottom-cornerRadius[6], rect.X, rect.Y+cornerRadius[7]);
|
||
path.AddArc(rect.X, rect.Y, 2f*cornerRadius[0], 2f*cornerRadius[7], 180, 90);
|
||
|
||
return path;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Helper function which draws a shadow of the rounded rect.
|
||
/// </summary>
|
||
/// <param name="rect">Rectangle coordinates.</param>
|
||
/// <param name="cornerRadius">Array of 4 corners radius.</param>
|
||
/// <param name="radius">Rounding radius.</param>
|
||
/// <param name="centerColor">Center color.</param>
|
||
/// <param name="surroundColor">Surrounding color.</param>
|
||
/// <param name="shadowScale">Shadow scale value.</param>
|
||
internal void DrawRoundedRectShadowAbs(RectangleF rect, float[] cornerRadius, float radius, Color centerColor, Color surroundColor, float shadowScale)
|
||
{
|
||
// Create rounded rectangle path
|
||
GraphicsPath path = CreateRoundedRectPath(rect, cornerRadius);
|
||
|
||
// Create gradient brush
|
||
PathGradientBrush shadowBrush = new PathGradientBrush(path);
|
||
shadowBrush.CenterColor = centerColor;
|
||
|
||
// Set the color along the entire boundary of the path
|
||
Color[] colors = {surroundColor};
|
||
shadowBrush.SurroundColors = colors;
|
||
shadowBrush.CenterPoint = new PointF(rect.X + rect.Width/2f, rect.Y + rect.Height/2f);
|
||
|
||
// Define brush focus scale
|
||
PointF focusScale = new PointF(1-shadowScale*radius/rect.Width, 1-shadowScale*radius/rect.Height);
|
||
shadowBrush.FocusScales = focusScale;
|
||
|
||
// Draw rounded rectangle
|
||
this.FillPath(shadowBrush, path);
|
||
|
||
if( path != null )
|
||
{
|
||
path.Dispose();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draws 3D border in absolute coordinates.
|
||
/// </summary>
|
||
/// <param name="borderSkin">Border skin object.</param>
|
||
/// <param name="rect">Rectangle of the border (pixel coordinates).</param>
|
||
/// <param name="backColor">Color of rectangle</param>
|
||
/// <param name="backHatchStyle">Hatch style</param>
|
||
/// <param name="backImage">Back Image</param>
|
||
/// <param name="backImageWrapMode">Image mode</param>
|
||
/// <param name="backImageTransparentColor">Image transparent color.</param>
|
||
/// <param name="backImageAlign">Image alignment</param>
|
||
/// <param name="backGradientStyle">Gradient type </param>
|
||
/// <param name="backSecondaryColor">Gradient End Color</param>
|
||
/// <param name="borderColor">Border Color</param>
|
||
/// <param name="borderWidth">Border Width</param>
|
||
/// <param name="borderDashStyle">Border Style</param>
|
||
internal void Draw3DBorderRel(
|
||
BorderSkin borderSkin,
|
||
RectangleF rect,
|
||
Color backColor,
|
||
ChartHatchStyle backHatchStyle,
|
||
string backImage,
|
||
ChartImageWrapMode backImageWrapMode,
|
||
Color backImageTransparentColor,
|
||
ChartImageAlignmentStyle backImageAlign,
|
||
GradientStyle backGradientStyle,
|
||
Color backSecondaryColor,
|
||
Color borderColor,
|
||
int borderWidth,
|
||
ChartDashStyle borderDashStyle)
|
||
{
|
||
Draw3DBorderAbs(borderSkin, GetAbsoluteRectangle(rect), backColor, backHatchStyle,
|
||
backImage, backImageWrapMode, backImageTransparentColor, backImageAlign, backGradientStyle,
|
||
backSecondaryColor, borderColor, borderWidth, borderDashStyle);
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// Draws 3D border in absolute coordinates.
|
||
/// </summary>
|
||
/// <param name="borderSkin">Border skin object.</param>
|
||
/// <param name="absRect">Rectangle of the border (pixel coordinates).</param>
|
||
/// <param name="backColor">Color of rectangle</param>
|
||
/// <param name="backHatchStyle">Hatch style</param>
|
||
/// <param name="backImage">Back Image</param>
|
||
/// <param name="backImageWrapMode">Image mode</param>
|
||
/// <param name="backImageTransparentColor">Image transparent color.</param>
|
||
/// <param name="backImageAlign">Image alignment</param>
|
||
/// <param name="backGradientStyle">Gradient type </param>
|
||
/// <param name="backSecondaryColor">Gradient End Color</param>
|
||
/// <param name="borderColor">Border Color</param>
|
||
/// <param name="borderWidth">Border Width</param>
|
||
/// <param name="borderDashStyle">Border Style</param>
|
||
internal void Draw3DBorderAbs(
|
||
BorderSkin borderSkin,
|
||
RectangleF absRect,
|
||
Color backColor,
|
||
ChartHatchStyle backHatchStyle,
|
||
string backImage,
|
||
ChartImageWrapMode backImageWrapMode,
|
||
Color backImageTransparentColor,
|
||
ChartImageAlignmentStyle backImageAlign,
|
||
GradientStyle backGradientStyle,
|
||
Color backSecondaryColor,
|
||
Color borderColor,
|
||
int borderWidth,
|
||
ChartDashStyle borderDashStyle)
|
||
{
|
||
// Check input parameters
|
||
if(_common == null || borderSkin.SkinStyle == BorderSkinStyle.None || absRect.Width == 0 || absRect.Height == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// Find required border interface
|
||
IBorderType borderTypeInterface = _common.BorderTypeRegistry.GetBorderType(borderSkin.SkinStyle.ToString());
|
||
if(borderTypeInterface != null)
|
||
{
|
||
borderTypeInterface.Resolution = this.Graphics.DpiX;
|
||
// Draw border
|
||
borderTypeInterface.DrawBorder(this, borderSkin, absRect, backColor, backHatchStyle, backImage, backImageWrapMode,
|
||
backImageTransparentColor, backImageAlign, backGradientStyle, backSecondaryColor,
|
||
borderColor, borderWidth, borderDashStyle);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Pie Method
|
||
|
||
/// <summary>
|
||
/// Helper function that retrieves pie drawing style.
|
||
/// </summary>
|
||
/// <param name="point">Data point to get the drawing style for.</param>
|
||
/// <returns>pie drawing style.</returns>
|
||
internal static PieDrawingStyle GetPieDrawingStyle(DataPoint point)
|
||
{
|
||
// Get column drawing style
|
||
PieDrawingStyle pieDrawingStyle = PieDrawingStyle.Default;
|
||
string styleName = point[CustomPropertyName.PieDrawingStyle];
|
||
if(styleName != null)
|
||
{
|
||
if(String.Compare(styleName, "Default", StringComparison.OrdinalIgnoreCase) == 0)
|
||
{
|
||
pieDrawingStyle = PieDrawingStyle.Default;
|
||
}
|
||
else if (String.Compare(styleName, "SoftEdge", StringComparison.OrdinalIgnoreCase) == 0)
|
||
{
|
||
pieDrawingStyle = PieDrawingStyle.SoftEdge;
|
||
}
|
||
else if (String.Compare(styleName, "Concave", StringComparison.OrdinalIgnoreCase) == 0)
|
||
{
|
||
pieDrawingStyle = PieDrawingStyle.Concave;
|
||
}
|
||
else
|
||
{
|
||
throw( new InvalidOperationException(SR.ExceptionCustomAttributeValueInvalid( styleName, "PieDrawingStyle")));
|
||
}
|
||
}
|
||
return pieDrawingStyle;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Draws a pie defined by an ellipse specified by a Rectangle structure and two radial lines.
|
||
/// </summary>
|
||
/// <param name="rect">Rectangle structure that represents the bounding rectangle that defines the ellipse from which the pie shape comes.</param>
|
||
/// <param name="startAngle">Angle measured in degrees clockwise from the x-axis to the first side of the pie shape.</param>
|
||
/// <param name="sweepAngle">Angle measured in degrees clockwise from the startAngle parameter to the second side of the pie shape.</param>
|
||
/// <param name="backColor">Fill color</param>
|
||
/// <param name="backHatchStyle">Fill Hatch Style</param>
|
||
/// <param name="backImage">Fill texture</param>
|
||
/// <param name="backImageWrapMode">Texture image mode</param>
|
||
/// <param name="backImageTransparentColor">Texture transparent color</param>
|
||
/// <param name="backGradientStyle">Fill Gradient type </param>
|
||
/// <param name="backSecondaryColor">Fill Gradient Second Color</param>
|
||
/// <param name="borderColor">Border Color</param>
|
||
/// <param name="borderWidth">Border Width</param>
|
||
/// <param name="borderDashStyle">Border Style</param>
|
||
/// <param name="shadow">True if shadow is active</param>
|
||
/// <param name="doughnut">True if Doughnut is drawn instead of pie</param>
|
||
/// <param name="doughnutRadius">Internal radius of the doughnut</param>
|
||
/// <param name="pieDrawingStyle">Pie drawing style.</param>
|
||
internal void DrawPieRel(
|
||
RectangleF rect,
|
||
float startAngle,
|
||
float sweepAngle,
|
||
Color backColor,
|
||
ChartHatchStyle backHatchStyle,
|
||
string backImage,
|
||
ChartImageWrapMode backImageWrapMode,
|
||
Color backImageTransparentColor,
|
||
GradientStyle backGradientStyle,
|
||
Color backSecondaryColor,
|
||
Color borderColor,
|
||
int borderWidth,
|
||
ChartDashStyle borderDashStyle,
|
||
bool shadow,
|
||
bool doughnut,
|
||
float doughnutRadius,
|
||
PieDrawingStyle pieDrawingStyle
|
||
)
|
||
{
|
||
Pen borderPen = null; // Pen
|
||
Brush fillBrush; // Brush
|
||
|
||
// Get absolute rectangle
|
||
RectangleF absRect = GetAbsoluteRectangle( rect );
|
||
|
||
if( doughnutRadius == 100.0 )
|
||
{
|
||
doughnut = false;
|
||
}
|
||
|
||
if( doughnutRadius == 0.0 )
|
||
{
|
||
return;
|
||
}
|
||
|
||
// Create Brush
|
||
if( backHatchStyle != ChartHatchStyle.None )
|
||
{
|
||
// Create Hatch Brush
|
||
fillBrush = GetHatchBrush( backHatchStyle, backColor, backSecondaryColor );
|
||
}
|
||
else if( backGradientStyle != GradientStyle.None )
|
||
{
|
||
// Create gradient brush
|
||
if( backGradientStyle == GradientStyle.Center )
|
||
{
|
||
fillBrush = GetPieGradientBrush( absRect, backColor, backSecondaryColor );
|
||
}
|
||
else
|
||
{
|
||
using (GraphicsPath path = new GraphicsPath())
|
||
{
|
||
path.AddPie(absRect.X, absRect.Y, absRect.Width, absRect.Height, startAngle, sweepAngle);
|
||
fillBrush = GetGradientBrush(path.GetBounds(), backColor, backSecondaryColor, backGradientStyle);
|
||
}
|
||
}
|
||
}
|
||
else if( backImage.Length > 0 && backImageWrapMode != ChartImageWrapMode.Unscaled && backImageWrapMode != ChartImageWrapMode.Scaled )
|
||
{
|
||
// Create textured brush
|
||
fillBrush = GetTextureBrush(backImage, backImageTransparentColor, backImageWrapMode, backColor );
|
||
}
|
||
else
|
||
{
|
||
// Create solid brush
|
||
fillBrush = new SolidBrush( backColor );
|
||
}
|
||
|
||
// Create border Pen
|
||
borderPen = new Pen( borderColor, borderWidth );
|
||
|
||
// Set a border line style
|
||
borderPen.DashStyle = GetPenStyle( borderDashStyle );
|
||
|
||
// Use rounded line joins
|
||
borderPen.LineJoin = LineJoin.Round;
|
||
|
||
// Draw Doughnut
|
||
if( doughnut )
|
||
{
|
||
using (GraphicsPath path = new GraphicsPath())
|
||
{
|
||
|
||
path.AddArc(absRect.X + absRect.Width * doughnutRadius / 200 - 1, absRect.Y + absRect.Height * doughnutRadius / 200 - 1, absRect.Width - absRect.Width * doughnutRadius / 100 + 2, absRect.Height - absRect.Height * doughnutRadius / 100 + 2, startAngle, sweepAngle);
|
||
path.AddArc(absRect.X, absRect.Y, absRect.Width, absRect.Height, startAngle + sweepAngle, -sweepAngle);
|
||
|
||
path.CloseFigure();
|
||
|
||
this.FillPath(fillBrush, path);
|
||
|
||
|
||
// Draw Pie gradien effects
|
||
this.DrawPieGradientEffects(pieDrawingStyle, absRect, startAngle, sweepAngle, doughnutRadius);
|
||
|
||
// Draw Doughnut Border
|
||
if (!shadow &&
|
||
borderWidth > 0 &&
|
||
borderDashStyle != ChartDashStyle.NotSet)
|
||
{
|
||
this.DrawPath(borderPen, path);
|
||
}
|
||
}
|
||
}
|
||
else // Draw Pie
|
||
{
|
||
|
||
// Draw Soft shadow for pie slice
|
||
if( shadow && softShadows )
|
||
{
|
||
DrawPieSoftShadow( startAngle, sweepAngle, absRect, backColor );
|
||
}
|
||
else
|
||
{
|
||
// Fill Pie for normal shadow or colored pie slice
|
||
this.FillPie( fillBrush, absRect.X, absRect.Y, absRect.Width, absRect.Height, startAngle, sweepAngle );
|
||
|
||
// Draw Pie gradien effects
|
||
this.DrawPieGradientEffects( pieDrawingStyle, absRect, startAngle, sweepAngle, -1f);
|
||
}
|
||
|
||
|
||
// Draw Pie Border
|
||
if( !shadow &&
|
||
borderWidth > 0 &&
|
||
borderDashStyle != ChartDashStyle.NotSet)
|
||
{
|
||
this.DrawPie( borderPen, absRect.X, absRect.Y, absRect.Width, absRect.Height, startAngle, sweepAngle );
|
||
}
|
||
}
|
||
|
||
// Dispose graphics objects
|
||
if( borderPen != null )
|
||
{
|
||
borderPen.Dispose();
|
||
}
|
||
|
||
if( fillBrush != null )
|
||
{
|
||
fillBrush.Dispose();
|
||
}
|
||
}
|
||
|
||
private void DrawPieGradientEffects(
|
||
PieDrawingStyle pieDrawingStyle,
|
||
RectangleF position,
|
||
float startAngle,
|
||
float sweepAngle,
|
||
float doughnutRadius)
|
||
{
|
||
if(pieDrawingStyle == PieDrawingStyle.Concave)
|
||
{
|
||
// Calculate the size of the shadow. Note: For Doughnut chart shadow is drawn
|
||
// twice on the outside and inside radius.
|
||
float minSize = (float)Math.Min(position.Width, position.Height);
|
||
float shadowSize = minSize * 0.05f;
|
||
|
||
// Create brush path
|
||
RectangleF gradientPath = position;
|
||
gradientPath.Inflate(-shadowSize, -shadowSize);
|
||
using(GraphicsPath brushPath = new GraphicsPath())
|
||
{
|
||
brushPath.AddEllipse(gradientPath);
|
||
|
||
// Create shadow path
|
||
using(GraphicsPath path = new GraphicsPath())
|
||
{
|
||
if(doughnutRadius < 0f)
|
||
{
|
||
path.AddPie(Rectangle.Round(gradientPath), startAngle, sweepAngle);
|
||
}
|
||
else
|
||
{
|
||
path.AddArc(
|
||
gradientPath.X + position.Width * doughnutRadius /200 - 1 - shadowSize,
|
||
gradientPath.Y + position.Height * doughnutRadius /200 - 1 - shadowSize,
|
||
gradientPath.Width - position.Width * doughnutRadius / 100 + 2 + 2f * shadowSize,
|
||
gradientPath.Height - position.Height * doughnutRadius / 100 + 2 + 2f * shadowSize,
|
||
startAngle,
|
||
sweepAngle );
|
||
path.AddArc( gradientPath.X, gradientPath.Y, gradientPath.Width, gradientPath.Height, startAngle + sweepAngle, -sweepAngle );
|
||
}
|
||
|
||
// Create linear gradient brush
|
||
gradientPath.Inflate(1f, 1f);
|
||
using(LinearGradientBrush brush = new LinearGradientBrush(
|
||
gradientPath,
|
||
Color.Red,
|
||
Color.Green,
|
||
LinearGradientMode.Vertical) )
|
||
{
|
||
ColorBlend colorBlend = new ColorBlend(3);
|
||
colorBlend.Colors[0] = Color.FromArgb(100, Color.Black);
|
||
colorBlend.Colors[1] = Color.Transparent;
|
||
colorBlend.Colors[2] = Color.FromArgb(140, Color.White);
|
||
colorBlend.Positions[0] = 0f;
|
||
colorBlend.Positions[1] = 0.5f;
|
||
colorBlend.Positions[2] = 1f;
|
||
brush.InterpolationColors = colorBlend;
|
||
|
||
// Fill shadow
|
||
this.FillPath( brush, path );
|
||
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if(pieDrawingStyle == PieDrawingStyle.SoftEdge)
|
||
{
|
||
// Calculate the size of the shadow. Note: For Doughnut chart shadow is drawn
|
||
// twice on the outside and inside radius.
|
||
float minSize = (float)Math.Min(position.Width, position.Height);
|
||
float shadowSize = minSize/10f;
|
||
if(doughnutRadius > 0f)
|
||
{
|
||
shadowSize = (minSize * doughnutRadius / 100f) / 8f;
|
||
}
|
||
|
||
// Create brush path
|
||
using(GraphicsPath brushPath = new GraphicsPath())
|
||
{
|
||
brushPath.AddEllipse(position);
|
||
|
||
// Create shadow path
|
||
using(GraphicsPath path = new GraphicsPath())
|
||
{
|
||
path.AddArc( position.X + shadowSize, position.Y + shadowSize, position.Width - shadowSize * 2f, position.Height - shadowSize * 2f, startAngle, sweepAngle );
|
||
path.AddArc( position.X, position.Y, position.Width, position.Height, startAngle + sweepAngle, -sweepAngle );
|
||
path.CloseFigure();
|
||
|
||
// Create shadow brush
|
||
using( PathGradientBrush brush = new PathGradientBrush(brushPath) )
|
||
{
|
||
brush.CenterColor = Color.Transparent;
|
||
brush.SurroundColors = new Color[] { Color.FromArgb(100, Color.Black) };
|
||
|
||
Blend blend = new Blend(3);
|
||
blend.Positions[0] = 0f;
|
||
blend.Factors[0] = 0f;
|
||
blend.Positions[1] = shadowSize / (minSize / 2f);
|
||
blend.Factors[1] = 1f;
|
||
blend.Positions[2] = 1f;
|
||
blend.Factors[2] = 1f;
|
||
brush.Blend = blend;
|
||
|
||
// Fill shadow
|
||
this.FillPath( brush, path );
|
||
}
|
||
}
|
||
|
||
// Draw inner shadow for the doughnut chart
|
||
if(doughnutRadius > 0f)
|
||
{
|
||
// Create brush path
|
||
using(GraphicsPath brushInsidePath = new GraphicsPath())
|
||
{
|
||
RectangleF innerPosition = position;
|
||
innerPosition.Inflate(- position.Width * doughnutRadius / 200f + shadowSize, -position.Height * doughnutRadius / 200f + shadowSize);
|
||
brushInsidePath.AddEllipse(innerPosition);
|
||
|
||
// Create shadow path
|
||
using(GraphicsPath path = new GraphicsPath())
|
||
{
|
||
path.AddArc( innerPosition.X + shadowSize, innerPosition.Y + shadowSize, innerPosition.Width - 2f * shadowSize, innerPosition.Height - 2f * shadowSize, startAngle, sweepAngle );
|
||
path.AddArc( innerPosition.X, innerPosition.Y, innerPosition.Width, innerPosition.Height, startAngle + sweepAngle, -sweepAngle );
|
||
path.CloseFigure();
|
||
|
||
// Create shadow brush
|
||
using( PathGradientBrush brushInner = new PathGradientBrush(brushInsidePath) )
|
||
{
|
||
brushInner.CenterColor = Color.FromArgb(100, Color.Black);
|
||
brushInner.SurroundColors = new Color[] { Color.Transparent };
|
||
|
||
Blend blend = new Blend(3);
|
||
blend.Positions[0] = 0f;
|
||
blend.Factors[0] = 0f;
|
||
blend.Positions[1] = shadowSize / (innerPosition.Width / 2f);
|
||
blend.Factors[1] = 1f;
|
||
blend.Positions[2] = 1f;
|
||
blend.Factors[2] = 1f;
|
||
brushInner.Blend = blend;
|
||
|
||
// Fill shadow
|
||
this.FillPath( brushInner, path );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// The soft shadow of the pie
|
||
/// </summary>
|
||
/// <param name="startAngle">Angle measured in degrees clockwise from the x-axis to the first side of the pie shape.</param>
|
||
/// <param name="sweepAngle">Angle measured in degrees clockwise from the startAngle parameter to the second side of the pie shape.</param>
|
||
/// <param name="absRect">Rectangle of the pie in absolute coordinates</param>
|
||
/// <param name="backColor">Fill color</param>
|
||
private void DrawPieSoftShadow( float startAngle, float sweepAngle, RectangleF absRect, Color backColor )
|
||
{
|
||
GraphicsPath path = new GraphicsPath();
|
||
|
||
path.AddEllipse( absRect.X, absRect.Y, absRect.Width, absRect.Height );
|
||
|
||
PathGradientBrush brush = new PathGradientBrush( path );
|
||
|
||
Color[] colors = {
|
||
Color.FromArgb( 0, backColor ),
|
||
Color.FromArgb( backColor.A, backColor ),
|
||
Color.FromArgb( backColor.A, backColor )};
|
||
|
||
float[] relativePositions = {
|
||
0f,
|
||
0.05f,
|
||
1.0f}; // at the center point.
|
||
|
||
ColorBlend colorBlend = new ColorBlend();
|
||
colorBlend.Colors = colors;
|
||
colorBlend.Positions = relativePositions;
|
||
brush.InterpolationColors = colorBlend;
|
||
|
||
this.FillPie( brush, absRect.X, absRect.Y, absRect.Width, absRect.Height, startAngle, sweepAngle );
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Arrow Methods
|
||
|
||
/// <summary>
|
||
/// Draw Arrow.
|
||
/// </summary>
|
||
/// <param name="position">Position of the arrow</param>
|
||
/// <param name="orientation">Orientation of the arrow - left, right, top, bottom </param>
|
||
/// <param name="type">Arrow style: Triangle, Sharp Triangle, Lines</param>
|
||
/// <param name="color">Color of the arrow</param>
|
||
/// <param name="lineWidth">Line width</param>
|
||
/// <param name="lineDashStyle">Line Dash style</param>
|
||
/// <param name="shift">Distance from the chart area</param>
|
||
/// <param name="size">Arrow size</param>
|
||
internal void DrawArrowRel( PointF position, ArrowOrientation orientation, AxisArrowStyle type, Color color, int lineWidth, ChartDashStyle lineDashStyle, double shift, double size )
|
||
{
|
||
// Check if arrow should be drawn
|
||
if(type == AxisArrowStyle.None)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// Set a color
|
||
using (SolidBrush brush = new SolidBrush(color))
|
||
{
|
||
PointF endPoint = PointF.Empty; // End point of axis line
|
||
PointF[] points; // arrow points
|
||
PointF absolutePosition; // Absolute position of axis
|
||
|
||
absolutePosition = GetAbsolutePoint(position);
|
||
|
||
// Arrow type is triangle
|
||
if (type == AxisArrowStyle.Triangle)
|
||
{
|
||
points = GetArrowShape(absolutePosition, orientation, shift, size, type, ref endPoint);
|
||
|
||
endPoint = GetRelativePoint(endPoint);
|
||
|
||
// Draw center line
|
||
DrawLineRel(color, lineWidth, lineDashStyle, position, endPoint);
|
||
|
||
// Draw arrow
|
||
this.FillPolygon(brush, points);
|
||
|
||
}
|
||
// Arrow type is sharp triangle
|
||
else if (type == AxisArrowStyle.SharpTriangle)
|
||
{
|
||
points = GetArrowShape(absolutePosition, orientation, shift, size, type, ref endPoint);
|
||
|
||
endPoint = GetRelativePoint(endPoint);
|
||
|
||
// Draw center line
|
||
DrawLineRel(color, lineWidth, lineDashStyle, position, endPoint);
|
||
|
||
// Draw arrow
|
||
this.FillPolygon(brush, points);
|
||
|
||
}
|
||
// Arrow type is 'Lines'
|
||
else if (type == AxisArrowStyle.Lines)
|
||
{
|
||
points = GetArrowShape(absolutePosition, orientation, shift, size, type, ref endPoint);
|
||
|
||
points[0] = GetRelativePoint(points[0]);
|
||
points[1] = GetRelativePoint(points[1]);
|
||
points[2] = GetRelativePoint(points[2]);
|
||
|
||
endPoint = GetRelativePoint(endPoint);
|
||
|
||
// Draw arrow
|
||
DrawLineRel(color, lineWidth, lineDashStyle, position, endPoint);
|
||
DrawLineRel(color, lineWidth, lineDashStyle, points[0], points[2]);
|
||
DrawLineRel(color, lineWidth, lineDashStyle, points[1], points[2]);
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// This function calculates points for polygon, which represents
|
||
/// shape of an arrow. There are four different orientations
|
||
/// of arrow and three arrow types.
|
||
/// </summary>
|
||
/// <param name="position">Arrow position</param>
|
||
/// <param name="orientation">Arrow orientation ( Left, Right, Top, Bottom )</param>
|
||
/// <param name="shift">Distance from chart area to the arrow</param>
|
||
/// <param name="size">Arrow size</param>
|
||
/// <param name="type">Arrow style.</param>
|
||
/// <param name="endPoint">End point of the axis and the beginning of arrow</param>
|
||
/// <returns>Polygon points</returns>
|
||
private PointF[] GetArrowShape( PointF position, ArrowOrientation orientation, double shift, double size, AxisArrowStyle type, ref PointF endPoint )
|
||
{
|
||
PointF[] points = new PointF[3]; // Polygon points
|
||
double sharp; // Size for sharp triangle
|
||
|
||
// Four different orientations for AxisArrowStyle
|
||
switch( orientation )
|
||
{
|
||
// Top orientation
|
||
case ArrowOrientation.Top:
|
||
// Get absolute size for arrow
|
||
// Arrow size has to have the same shape when width and height
|
||
// are changed. When the picture is resized, width of the chart
|
||
// picture is used only for arrow size.
|
||
size = GetAbsoluteSize( new SizeF((float)size, (float)size) ).Width;
|
||
shift = GetAbsoluteSize( new SizeF((float)shift,(float)shift) ).Height;
|
||
|
||
// Size for sharp and regular triangle
|
||
if( type == AxisArrowStyle.SharpTriangle )
|
||
sharp = size * 4;
|
||
else
|
||
sharp = size * 2;
|
||
|
||
points[0].X = position.X - (float)size;
|
||
points[0].Y = position.Y - (float)shift;
|
||
points[1].X = position.X + (float)size;
|
||
points[1].Y = position.Y - (float)shift;
|
||
points[2].X = position.X;
|
||
points[2].Y = position.Y - (float)shift - (float)sharp;
|
||
// End of the axis line
|
||
endPoint.X = position.X;
|
||
if( type == AxisArrowStyle.SharpTriangle || type == AxisArrowStyle.Triangle )
|
||
endPoint.Y = points[1].Y;
|
||
else
|
||
endPoint.Y = points[2].Y;
|
||
|
||
break;
|
||
// Bottom orientation
|
||
case ArrowOrientation.Bottom:
|
||
// Get absolute size for arrow
|
||
// Arrow size has to have the same shape when width and height
|
||
// are changed. When the picture is resized, width of the chart
|
||
// picture is used only for arrow size.
|
||
size = GetAbsoluteSize( new SizeF((float)size, (float)size) ).Width;
|
||
shift = GetAbsoluteSize( new SizeF((float)shift,(float)shift) ).Height;
|
||
|
||
// Size for sharp and regular triangle
|
||
if( type == AxisArrowStyle.SharpTriangle )
|
||
sharp = size * 4;
|
||
else
|
||
sharp = size * 2;
|
||
|
||
points[0].X = position.X - (float)size;
|
||
points[0].Y = position.Y + (float)shift;
|
||
points[1].X = position.X + (float)size;
|
||
points[1].Y = position.Y + (float)shift;
|
||
points[2].X = position.X;
|
||
points[2].Y = position.Y + (float)shift + (float)sharp;
|
||
// End of the axis line
|
||
endPoint.X = position.X;
|
||
if( type == AxisArrowStyle.SharpTriangle || type == AxisArrowStyle.Triangle )
|
||
endPoint.Y = points[1].Y;
|
||
else
|
||
endPoint.Y = points[2].Y;
|
||
break;
|
||
// Left orientation
|
||
case ArrowOrientation.Left:
|
||
// Get absolute size for arrow
|
||
size = GetAbsoluteSize( new SizeF((float)size, (float)size) ).Width;
|
||
shift = GetAbsoluteSize( new SizeF((float)shift,(float)shift) ).Width;
|
||
|
||
// Size for sharp and regular triangle
|
||
if( type == AxisArrowStyle.SharpTriangle )
|
||
sharp = size * 4;
|
||
else
|
||
sharp = size * 2;
|
||
|
||
points[0].Y = position.Y - (float)size;
|
||
points[0].X = position.X - (float)shift;
|
||
points[1].Y = position.Y + (float)size;
|
||
points[1].X = position.X - (float)shift;
|
||
points[2].Y = position.Y;
|
||
points[2].X = position.X - (float)shift - (float)sharp;
|
||
// End of the axis line
|
||
endPoint.Y = position.Y;
|
||
if( type == AxisArrowStyle.SharpTriangle || type == AxisArrowStyle.Triangle )
|
||
endPoint.X = points[1].X;
|
||
else
|
||
endPoint.X = points[2].X;
|
||
break;
|
||
// Right orientation
|
||
case ArrowOrientation.Right:
|
||
// Get absolute size for arrow
|
||
size = GetAbsoluteSize( new SizeF((float)size, (float)size) ).Width;
|
||
shift = GetAbsoluteSize( new SizeF((float)shift,(float)shift) ).Width;
|
||
|
||
// Size for sharp and regular triangle
|
||
if( type == AxisArrowStyle.SharpTriangle )
|
||
sharp = size * 4;
|
||
else
|
||
sharp = size * 2;
|
||
|
||
points[0].Y = position.Y - (float)size;
|
||
points[0].X = position.X + (float)shift;
|
||
points[1].Y = position.Y + (float)size;
|
||
points[1].X = position.X + (float)shift;
|
||
points[2].Y = position.Y;
|
||
points[2].X = position.X + (float)shift + (float)sharp;
|
||
// End of the axis line
|
||
endPoint.Y = position.Y;
|
||
if( type == AxisArrowStyle.SharpTriangle || type == AxisArrowStyle.Triangle )
|
||
endPoint.X = points[1].X;
|
||
else
|
||
endPoint.X = points[2].X;
|
||
break;
|
||
}
|
||
|
||
return points;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Other methods and properties
|
||
|
||
/// <summary>
|
||
/// Helper function that retrieves bar drawing style.
|
||
/// </summary>
|
||
/// <param name="point">Data point to get the drawing style for.</param>
|
||
/// <returns>Bar drawing style.</returns>
|
||
internal static BarDrawingStyle GetBarDrawingStyle(DataPoint point)
|
||
{
|
||
// Get column drawing style
|
||
BarDrawingStyle barDrawingStyle = BarDrawingStyle.Default;
|
||
string styleName = point[CustomPropertyName.DrawingStyle];
|
||
if(styleName != null)
|
||
{
|
||
if(String.Compare(styleName, "Default", StringComparison.OrdinalIgnoreCase) == 0)
|
||
{
|
||
barDrawingStyle = BarDrawingStyle.Default;
|
||
}
|
||
else if (String.Compare(styleName, "Cylinder", StringComparison.OrdinalIgnoreCase) == 0)
|
||
{
|
||
barDrawingStyle = BarDrawingStyle.Cylinder;
|
||
}
|
||
else if (String.Compare(styleName, "Emboss", StringComparison.OrdinalIgnoreCase) == 0)
|
||
{
|
||
barDrawingStyle = BarDrawingStyle.Emboss;
|
||
}
|
||
else if (String.Compare(styleName, "LightToDark", StringComparison.OrdinalIgnoreCase) == 0)
|
||
{
|
||
barDrawingStyle = BarDrawingStyle.LightToDark;
|
||
}
|
||
else if (String.Compare(styleName, "Wedge", StringComparison.OrdinalIgnoreCase) == 0)
|
||
{
|
||
barDrawingStyle = BarDrawingStyle.Wedge;
|
||
}
|
||
else
|
||
{
|
||
throw (new InvalidOperationException(SR.ExceptionCustomAttributeValueInvalid(styleName, "DrawingStyle")));
|
||
}
|
||
}
|
||
return barDrawingStyle;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// Find rounding coordinates for a rectangle
|
||
/// </summary>
|
||
/// <param name="rect">Rectangle which has to be rounded</param>
|
||
/// <returns>Rounded rectangle</returns>
|
||
internal RectangleF Round(RectangleF rect)
|
||
{
|
||
float left = (float)Math.Round( (double)rect.Left );
|
||
float right = (float)Math.Round( (double)rect.Right );
|
||
float top = (float)Math.Round( (double)rect.Top );
|
||
float bottom = (float)Math.Round( (double)rect.Bottom );
|
||
|
||
return new RectangleF( left, top, right - left, bottom - top );
|
||
}
|
||
|
||
/// <summary>
|
||
/// This method takes a given axis value for a specified axis and returns the relative pixel value.
|
||
/// </summary>
|
||
/// <param name="chartAreaName">Chart area name.</param>
|
||
/// <param name="axis">An AxisName enum value that identifies the relevant axis.</param>
|
||
/// <param name="axisValue">The axis value that needs to be converted to a relative pixel value.</param>
|
||
/// <returns>The converted axis value, in relative pixel coordinates.</returns>
|
||
public double GetPositionFromAxis( string chartAreaName, AxisName axis, double axisValue )
|
||
{
|
||
if( axis == AxisName.X )
|
||
return _common.ChartPicture.ChartAreas[chartAreaName].AxisX.GetLinearPosition( axisValue );
|
||
|
||
if( axis == AxisName.X2 )
|
||
return _common.ChartPicture.ChartAreas[chartAreaName].AxisX2.GetLinearPosition( axisValue );
|
||
|
||
if( axis == AxisName.Y )
|
||
return _common.ChartPicture.ChartAreas[chartAreaName].AxisY.GetLinearPosition( axisValue );
|
||
|
||
if( axis == AxisName.Y2 )
|
||
return _common.ChartPicture.ChartAreas[chartAreaName].AxisY2.GetLinearPosition( axisValue );
|
||
|
||
return 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Set picture size
|
||
/// </summary>
|
||
/// <param name="width">Width</param>
|
||
/// <param name="height">Height</param>
|
||
internal void SetPictureSize( int width, int height )
|
||
{
|
||
this._width = width;
|
||
this._height = height;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Constructor
|
||
/// </summary>
|
||
/// <param name="common">Common elements class</param>
|
||
internal ChartGraphics(CommonElements common)
|
||
{
|
||
// Set Common elements
|
||
this._common = common;
|
||
base.Common = common;
|
||
// Create a pen object
|
||
_pen = new Pen(Color.Black);
|
||
|
||
// Create a brush object
|
||
_solidBrush = new SolidBrush(Color.Black);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Chart Graphics Anti alias mode
|
||
/// </summary>
|
||
internal AntiAliasingStyles AntiAliasing
|
||
{
|
||
get
|
||
{
|
||
return _antiAliasing;
|
||
}
|
||
set
|
||
{
|
||
_antiAliasing = value;
|
||
|
||
// Graphics mode not set
|
||
if( Graphics == null )
|
||
return;
|
||
|
||
// Convert Chart's anti alias enumeration to GDI+ SmoothingMode
|
||
if( (_antiAliasing & AntiAliasingStyles.Graphics) == AntiAliasingStyles.Graphics )
|
||
{
|
||
this.SmoothingMode = SmoothingMode.AntiAlias;
|
||
}
|
||
else
|
||
{
|
||
this.SmoothingMode = SmoothingMode.None;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Gets reusable pen.
|
||
/// </summary>
|
||
internal Pen Pen
|
||
{
|
||
get { return _pen; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// Sets the clipping region of this Graphics object
|
||
/// to the rectangle specified by a RectangleF structure.
|
||
/// </summary>
|
||
/// <param name="region">Region rectangle</param>
|
||
internal void SetClip( RectangleF region )
|
||
{
|
||
this.SetClipAbs( GetAbsoluteRectangle( region ) );
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Color manipulation methods
|
||
|
||
/// <summary>
|
||
/// Returns the gradient color from a gradient position.
|
||
/// </summary>
|
||
/// <param name="beginColor">The color from the gradient beginning</param>
|
||
/// <param name="endColor">The color from the gradient end.</param>
|
||
/// <param name="relativePosition">The relative position.</param>
|
||
/// <returns>Result color.</returns>
|
||
static internal Color GetGradientColor(Color beginColor, Color endColor, double relativePosition)
|
||
{
|
||
// Check if position is valid
|
||
if(relativePosition < 0 || relativePosition > 1 || double.IsNaN(relativePosition))
|
||
{
|
||
return beginColor;
|
||
}
|
||
|
||
// Extracts Begin color
|
||
int nBRed = beginColor.R;
|
||
int nBGreen = beginColor.G;
|
||
int nBBlue = beginColor.B;
|
||
|
||
// Extracts End color
|
||
int nERed = endColor.R;
|
||
int nEGreen = endColor.G;
|
||
int nEBlue = endColor.B;
|
||
|
||
// Gradient positions for Red, Green and Blue colors
|
||
double dRRed = nBRed + (nERed - nBRed) * relativePosition;
|
||
double dRGreen = nBGreen + (nEGreen - nBGreen) * relativePosition;
|
||
double dRBlue = nBBlue + (nEBlue - nBBlue) * relativePosition;
|
||
|
||
// Make sure colors are in range from 0 to 255
|
||
if(dRRed > 255.0)
|
||
dRRed = 255.0;
|
||
if(dRRed < 0.0)
|
||
dRRed = 0.0;
|
||
if(dRGreen > 255.0)
|
||
dRGreen = 255.0;
|
||
if(dRGreen < 0.0)
|
||
dRGreen = 0.0;
|
||
if(dRBlue > 255.0)
|
||
dRBlue = 255.0;
|
||
if(dRBlue < 0.0)
|
||
dRBlue = 0.0;
|
||
|
||
// Return a gradient color position
|
||
return Color.FromArgb(beginColor.A, (int)dRRed, (int)dRGreen, (int)dRBlue);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region RightToLeft
|
||
/// <summary>
|
||
/// Returns chart right to left flag
|
||
/// </summary>
|
||
internal bool IsRightToLeft
|
||
{
|
||
get
|
||
{
|
||
if (Common == null)
|
||
{
|
||
return false;
|
||
}
|
||
return Common.ChartPicture.RightToLeft == RightToLeft.Yes;
|
||
}
|
||
}
|
||
|
||
#endregion //RightToLeft
|
||
|
||
#region IDisposable Members
|
||
/// <summary>
|
||
/// Releases unmanaged and - optionally - managed resources
|
||
/// </summary>
|
||
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||
protected override void Dispose(bool disposing)
|
||
{
|
||
if (disposing)
|
||
{
|
||
// Free up managed resources
|
||
if (_pen != null)
|
||
{
|
||
_pen.Dispose();
|
||
_pen = null;
|
||
}
|
||
if (_solidBrush != null)
|
||
{
|
||
_solidBrush.Dispose();
|
||
_solidBrush = null;
|
||
}
|
||
if (_myMatrix != null)
|
||
{
|
||
_myMatrix.Dispose();
|
||
_myMatrix = null;
|
||
}
|
||
}
|
||
base.Dispose(disposing);
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
}
|