/*
Copyright (C) 2008 Webyog Softworks Private Limited
This file is a part of Visifire Charts.
Visifire is a free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
You should have received a copy of the GNU General Public License
along with Visifire Charts. If not, see .
If GPL is not suitable for your products or company, Webyog provides Visifire
under a flexible commercial license designed to meet your specific usage and
distribution requirements. If you have already obtained a commercial license
from Webyog, you can use this file under those license terms.
*/
#if WPF
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Diagnostics;
#else
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Diagnostics;
#endif
namespace Visifire.Charts
{
///
/// Visifire.Charts.AxisLabel class
///
internal class AxisLabel
{
#region Public Methods
///
/// Initializes a new instance of the Visifire.Charts.AxisLabel class
///
public AxisLabel()
{
}
#endregion
#region Public Properties
#endregion
#region Public Events
#endregion
#region Protected Methods
#endregion
#region Internal Properties
///
/// Angle of the label has to be between -90 to 90
///
internal Double Angle
{
get
{
return _angle;
}
set
{
if (value >= -90 && value <= 90)
_angle = value;
else if (double.IsNaN(value))
_angle = 0;
else
{
Debug.Assert(false, "Label angle must be between -90 and 90");
}
}
}
///
/// The point w.r.t which the label will be drawn
///
internal Point Position
{
get
{
return _position;
}
set
{
_position = value;
}
}
///
/// Text content for the label
///
internal String Text
{
get
{
return _text;
}
set
{
_text = value;
}
}
///
/// Determines how the label should be placed around the point given in Position property
///
///
internal PlacementTypes Placement
{
get;
set;
}
///
/// Visual label element
///
internal Canvas Visual
{
get;
set;
}
///
/// Returns the top most point of the axis label canvas
///
internal Double ActualTop
{
get
{
return _actualTop;
}
private set
{
_actualTop = value;
}
}
///
/// Returns the left most point of the axis label canvas
///
internal Double ActualLeft
{
get
{
return _actualLeft;
}
private set
{
_actualLeft = value;
}
}
///
/// Returns the height of the axis label canvas
///
internal Double ActualHeight
{
get
{
return _actualHeight;
}
private set
{
_actualHeight = value;
}
}
///
/// Returns the width of the axis label canvas
///
internal Double ActualWidth
{
get
{
return _actualWidth;
}
private set
{
_actualWidth = value;
}
}
///
/// Width of the text element
///
internal Double ActualTextWidth
{
get
{
return _actualTextWidth;
}
private set
{
_actualTextWidth = value;
}
}
///
/// Height of the text element
///
internal Double ActualTextHeight
{
get
{
return _actualTextHeight;
}
private set
{
_actualTextHeight = value;
}
}
#region Font Properties
///
/// TextAlignment of the axis labels
///
internal TextAlignment TextAlignment
{
get;
set;
}
///
/// Font size of the axis labels
///
internal Double FontSize
{
get;
set;
}
///
/// Font family of the axis labels
///
internal FontFamily FontFamily
{
get;
set;
}
///
/// Font Color of the axis labels
///
internal Brush FontColor
{
get;
set;
}
///
/// Font Style of the axis labels
///
internal FontStyle FontStyle
{
get;
set;
}
///
/// Font Weight of the axis labels
///
internal FontWeight FontWeight
{
get;
set;
}
#endregion
#endregion
#region Private Delegates
#endregion
#region Private Properties
///
/// Visual text element for the label
///
internal TextBlock TextElement
{
get;
set;
}
///
/// To apply rotation to the text element
///
private RotateTransform Rotation
{
get;
set;
}
#endregion
#region Private Methods
///
/// Set the position of the label based on the angle and the Position Property
///
private void SetPosition()
{
// Depending on the placement type call the method for positioning of he labels
switch (Placement)
{
case PlacementTypes.Top:
// set the position for axis that will be placed to the top of plot area
SetPositionTop(Position);
break;
case PlacementTypes.Bottom:
// set the position for axis that will be placed to the bottom of plot area
SetPositionBottom(Position);
break;
case PlacementTypes.Left:
// set the position for axis that will be placed to the left of plot area
SetPositionLeft(Position);
break;
case PlacementTypes.Right:
// set the position for axis that will be placed to the right of plot area
SetPositionRight(Position);
break;
}
}
///
/// Set the position for axis that will be placed to the right of plot area
///
///
private void SetPositionRight(Point newPos)
{
// Stores the top and left of the textblock
Double top;
Double left;
// The angle made by the diagonal (passing from the top left corner to center right) with the horizontal
Double relativeAngle = Math.Atan(ActualTextHeight / (2 * ActualTextWidth));
// length of the diagonal (passing from the top left corner to center right)
Double length = Math.Sqrt(Math.Pow(ActualTextHeight / 2, 2) + Math.Pow(ActualTextWidth, 2));
// Gets the positive value of the angle
Double angle = GetAngle(Angle);
// Set the transform angle
Rotation.Angle = angle;
// Set the transform position
Rotation.CenterX = 0;
Rotation.CenterY = 0.5;
// calculate the top and left for the TextBlock
top = (ActualTextHeight / 2) * Math.Cos(GetRadians(angle));
left = -(ActualTextHeight / 2) * Math.Sin(GetRadians(angle));
// set the top and left for the AxisLabel element
Visual.SetValue(Canvas.LeftProperty, newPos.X - left);
Visual.SetValue(Canvas.TopProperty, newPos.Y - top);
if (angle > 90)
{
// If the angle is > 90 this means the title will be slanting downward towards right of the point specified in position
// Hence the actual left of the axis label element does not change
ActualTop = (Double)Visual.GetValue(Canvas.TopProperty) - top + length * Math.Sin(GetRadians(angle) + relativeAngle);
ActualLeft = (Double)Visual.GetValue(Canvas.LeftProperty);
}
else
{
// If the angle is <= 90 this means the title will be slanting upwards towards right of the point specified in position
// Hence the actual top of the axis label element does not change
ActualTop = (Double)Visual.GetValue(Canvas.TopProperty);
ActualLeft = (Double)Visual.GetValue(Canvas.LeftProperty) + left - (ActualTextHeight / 2) * Math.Cos(GetRadians(90 - angle));
}
}
///
/// Set the position for axis that will be placed to the left of plot area
///
///
private void SetPositionLeft(Point newPos)
{
// Stores the top and left of the textblock
Double top;
Double left;
// Get the angle in radians
Double radians = GetRadians(Angle);
// The angle made by the diagonal (passing from the top left corner to center right) with the horizontal
Double relativeAngle = Math.Atan(ActualTextHeight / (2 * ActualTextWidth));
// length of the diagonal (passing from the top left corner to center right)
Double length = Math.Sqrt(Math.Pow(ActualTextHeight / 2, 2) + Math.Pow(ActualTextWidth, 2));
// Gets the positive value of the angle
Double angle = GetAngle(Angle);
// Set the transform angle
Rotation.Angle = angle;
// Set the transform position
Rotation.CenterX = 0;
Rotation.CenterY = 0.5;
// calculate the top and left for the TextBlock
left = length * Math.Cos(radians + relativeAngle);
top = length * Math.Sin(radians + relativeAngle);
// set the top and left for the AxisLabel element
Visual.SetValue(Canvas.LeftProperty, newPos.X - left);
Visual.SetValue(Canvas.TopProperty, newPos.Y - top);
if (angle > 90)
{
// If the angle is > 90 this means the title will be slanting upwards towards left of the point specified in position
// Hence the actual left of the axis label element does not change
ActualTop = (Double)Visual.GetValue(Canvas.TopProperty) + top - ((ActualTextHeight / 2) * Math.Sin(GetRadians(90 - angle)));
ActualLeft = (Double)Visual.GetValue(Canvas.LeftProperty);
}
else
{
// If the angle is <= 90 this means the title will be slanting downwards towards left of the point specified in position
// Hence the actual top of the axis label element does not change
ActualTop = (Double)Visual.GetValue(Canvas.TopProperty);
ActualLeft = (Double)Visual.GetValue(Canvas.LeftProperty) + left - ((ActualTextHeight / 2) * Math.Cos(GetRadians(90 - Angle))); ;
}
}
///
/// Set the position for axis that will be placed to the bottom of plot area
///
///
private void SetPositionBottom(Point newPos)
{
// Stores the top and left of the textblock
Double top;
Double left;
// Gets the positive value of the angle
Double angle = GetAngle(Angle);
// if the angle is zero then do not apply rotation logic just get the horizontal center and place it
// using absolute value
if (angle == 0)
{
// Get the horizontal center
left = ActualTextWidth / 2;
// set the top to zero
top = 0;
// set rotation to zero
Rotation.Angle = 0;
// set the top and left for the AxisLabel element
Visual.SetValue(Canvas.LeftProperty, newPos.X - left);
Visual.SetValue(Canvas.TopProperty, newPos.Y + top);
// set the actual Top, left same as the visual top and left
ActualTop = (Double)Visual.GetValue(Canvas.TopProperty);
ActualLeft = (Double)Visual.GetValue(Canvas.LeftProperty);
}
else if (angle > 90)
{
// this is same as placing the axis label to the left
SetPositionLeft(newPos);
}
else
{
// this is same as placing the axis label to the right
SetPositionRight(newPos);
}
}
///
/// Set the position for axis that will be placed to the top of plot area
///
///
private void SetPositionTop(Point newPos)
{
// Stores the top and left of the textblock
Double top;
Double left;
// Gets the positive value of the angle
Double angle = GetAngle(Angle);
// if the angle is zero the do not apply rotation logic just get the horizontal center and place it
// using absolute value
if (angle == 0)
{
// Get the horizontal center
left = ActualTextWidth / 2;
// set the top same as the text height
top = ActualTextHeight;
// set rotation to zero
Rotation.Angle = 0;
// set the top and left for the AxisLabel element
Visual.SetValue(Canvas.LeftProperty, newPos.X - left);
Visual.SetValue(Canvas.TopProperty, newPos.Y - top);
// set the actual Top, left same as the visual top and left
ActualTop = (Double)Visual.GetValue(Canvas.TopProperty);
ActualLeft = (Double)Visual.GetValue(Canvas.LeftProperty);
}
else if (angle > 90)
{
// this is same as placing the axis label to the right
SetPositionRight(newPos);
}
else
{
// this is same as placing the axis label to the left
SetPositionLeft(newPos);
}
}
///
/// Calculates the ActualHeight and ActialWidth of the AxisLabel
///
///
private void CalculateSize(Double radianAngle)
{
// length of the diagonal from top left to bottom right
Double length = Math.Sqrt(Math.Pow(ActualTextHeight, 2) + Math.Pow(ActualTextWidth, 2));
// angle made by the diagonal with respect to the horizontal
Double beta = Math.Atan(ActualTextHeight / ActualTextWidth);
// calculate the two possible height and width values using the diagonal length and angle
Double height1 = length * Math.Sin(radianAngle + beta);
Double height2 = length * Math.Sin(radianAngle - beta);
Double width1 = length * Math.Cos(radianAngle + beta);
Double width2 = length * Math.Cos(radianAngle - beta);
// Actual height will be the maximum of the two calculated heights
ActualHeight = Math.Max(Math.Abs(height1), Math.Abs(height2));
// Actual width will be the maximum of the two calculated widths
ActualWidth = Math.Max(Math.Abs(width1), Math.Abs(width2));
}
///
/// Calculate the visual size of the text block
///
private void CalculateTextElementSize()
{
// Get the visual size of the text block
#if WPF
TextElement.Measure(new Size(Double.MaxValue, Double.MaxValue));
ActualTextHeight = TextElement.DesiredSize.Height;
ActualTextWidth = TextElement.DesiredSize.Width;
#else
ActualTextHeight = TextElement.ActualHeight;
ActualTextWidth = TextElement.ActualWidth;
#endif
}
#endregion
#region Internal Methods
///
/// Applies properties to the TextBlock
///
///
internal void ApplyProperties(AxisLabel axisLabel)
{
TextElement.Text = axisLabel.Text;
TextElement.Foreground = FontColor;
TextElement.FontSize = FontSize;
TextElement.FontFamily = FontFamily;
TextElement.FontStyle = FontStyle;
TextElement.FontWeight = FontWeight;
TextElement.TextAlignment = TextAlignment;
}
///
/// Create visual for AxisLabel
///
internal void CreateVisualObject(Boolean positioningAllowed, ElementData tag)
{
Visual = new Canvas() { Tag = tag };
TextElement = new TextBlock(){ Tag = tag };
Rotation = new RotateTransform();
TextElement.RenderTransform = Rotation;
Visual.Children.Add(TextElement);
ApplyProperties(this);
CalculateTextElementSize();
if(positioningAllowed)
SetPosition();
// calculate the actual size of the AxisLabel element
CalculateSize(GetRadians(Angle));
}
#endregion
#region Internal Events
#endregion
#region Static Methods
///
/// Returns a positive angle.
/// The angle must always be between -90 and 90
///
/// Angle
/// Angle as Double
internal static Double GetAngle(Double angle)
{
return (angle >= 0 ? angle : 360 + angle);
}
///
/// Returns the radian for the angle.
/// Internal calls angle to get positive angle value
///
/// Angle
/// Angle as Double
internal static Double GetRadians(Double angle)
{
return Math.PI / 180 * GetAngle(angle);
}
#endregion
#region Data
///
/// Identifier for Angle property
///
private Double _angle;
///
/// Identifier for Position property
///
private Point _position;
///
/// Identifier for Text property
///
private String _text;
///
/// Identifier for ActualLeft property
///
private Double _actualLeft;
///
/// Identifier for ActualTop property
///
private Double _actualTop;
///
/// Identifier for ActualWidth property
///
private Double _actualWidth;
///
/// Identifier for ActualHeight property
///
private Double _actualHeight;
///
/// Identifier for ActualTextHeight property
///
private Double _actualTextHeight;
///
/// Identifier for ActualTextWidth property
///
private Double _actualTextWidth;
#endregion
}
}